diff --git a/.changeset/good-roses-smash.md b/.changeset/good-roses-smash.md new file mode 100644 index 00000000000..3efa2dd439f --- /dev/null +++ b/.changeset/good-roses-smash.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#internal Fix Contract Reader data word index calculation and change ccip contract reader config for more optimal querying. diff --git a/.changeset/great-spiders-greet.md b/.changeset/great-spiders-greet.md new file mode 100644 index 00000000000..cd8e20a32a6 --- /dev/null +++ b/.changeset/great-spiders-greet.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +Fixed outdated headtracker config doc. #internal diff --git a/.changeset/neat-singers-notice.md b/.changeset/neat-singers-notice.md new file mode 100644 index 00000000000..6eff83c0c15 --- /dev/null +++ b/.changeset/neat-singers-notice.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +Added multiple blocks history estimator feature and config for Solana TXM. #added diff --git a/.changeset/six-wombats-shake.md b/.changeset/six-wombats-shake.md new file mode 100644 index 00000000000..448cf03bd51 --- /dev/null +++ b/.changeset/six-wombats-shake.md @@ -0,0 +1,5 @@ +--- +"chainlink": patch +--- + +CCIP token transfer integration tests #internal diff --git a/.github/workflows/build-publish-develop-pr.yml b/.github/workflows/build-publish-develop-pr.yml index fdd64e9fac6..caf46c1a3ed 100644 --- a/.github/workflows/build-publish-develop-pr.yml +++ b/.github/workflows/build-publish-develop-pr.yml @@ -56,15 +56,13 @@ jobs: - uses: actions/cache/restore@v4 with: - path: dist/linux_arm64 + path: dist/linux_arm64_v8.0 key: chainlink-arm64-${{ github.sha }} fail-on-cache-miss: true - name: Merge images for both architectures uses: ./.github/actions/goreleaser-build-sign-publish with: - # Temporary pin to working version as 2.4.2+ is breaking arm64 builds - goreleaser-version: "v2.3.2" docker-registry: ${{ secrets.AWS_SDLC_ECR_HOSTNAME }} docker-image-tag: ${{ needs.image-tag.outputs.image-tag }} goreleaser-release-type: "merge" @@ -88,7 +86,7 @@ jobs: - runner: ubuntu-24.04-4cores-16GB-ARM goarch: arm64 - dist_name: linux_arm64 + dist_name: linux_arm64_v8.0 steps: - name: Checkout repository uses: actions/checkout@v4.2.1 @@ -114,8 +112,6 @@ jobs: uses: ./.github/actions/goreleaser-build-sign-publish if: steps.cache.outputs.cache-hit != 'true' with: - # Temporary pin to working version as 2.4.2+ is breaking arm64 builds - goreleaser-version: "v2.3.2" docker-registry: ${{ secrets.AWS_SDLC_ECR_HOSTNAME }} docker-image-tag: ${{ needs.image-tag.outputs.image-tag }} goreleaser-release-type: ${{ needs.image-tag.outputs.release-type }} diff --git a/.github/workflows/build-publish-goreleaser.yml b/.github/workflows/build-publish-goreleaser.yml index 6ec4271c13d..8e61b84c4ad 100644 --- a/.github/workflows/build-publish-goreleaser.yml +++ b/.github/workflows/build-publish-goreleaser.yml @@ -59,7 +59,7 @@ jobs: - uses: actions/cache/restore@v4 with: - path: dist/linux_arm64 + path: dist/linux_arm64_v8.0 key: chainlink-arm64-${{ github.sha }}-${{ github.ref_name }} fail-on-cache-miss: true @@ -67,8 +67,6 @@ jobs: id: goreleaser-build-sign-publish uses: ./.github/actions/goreleaser-build-sign-publish with: - # Temporary pin to working version as 2.4.2+ is breaking arm64 builds - goreleaser-version: "v2.3.2" docker-registry: ${{ env.ECR_HOSTNAME }} docker-image-tag: ${{ github.ref_name }} goreleaser-config: .goreleaser.production.yaml @@ -89,7 +87,7 @@ jobs: - runner: ubuntu-24.04-4cores-16GB-ARM goarch: arm64 - dist_name: linux_arm64 + dist_name: linux_arm64_v8.0 environment: build-publish permissions: id-token: write @@ -121,8 +119,6 @@ jobs: if: steps.cache.outputs.cache-hit != 'true' uses: ./.github/actions/goreleaser-build-sign-publish with: - # Temporary pin to working version as 2.4.2+ is breaking arm64 builds - goreleaser-version: "v2.3.2" docker-registry: ${{ env.ECR_HOSTNAME }} docker-image-tag: ${{ github.ref_name }} goreleaser-release-type: release diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index f4481408005..19d17ddb588 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -30,6 +30,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4.2.1 + with: + fetch-depth: 0 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: files-changed diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 722c982b562..fb826b0f185 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -118,6 +118,8 @@ jobs: run: pnpm lint - name: Run solhint run: pnpm solhint + - name: Run solhint on tests + run: pnpm solhint-test prettier: defaults: diff --git a/contracts/.changeset/chatty-feet-clean.md b/contracts/.changeset/chatty-feet-clean.md deleted file mode 100644 index 161bfee8acd..00000000000 --- a/contracts/.changeset/chatty-feet-clean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal address security vulnerabilities around updating nodes and node operators on capabilities registry diff --git a/contracts/.changeset/chilled-melons-warn.md b/contracts/.changeset/chilled-melons-warn.md deleted file mode 100644 index f94192bb60c..00000000000 --- a/contracts/.changeset/chilled-melons-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal index don ID in ConfigSet event diff --git a/contracts/.changeset/dull-plums-flash.md b/contracts/.changeset/dull-plums-flash.md new file mode 100644 index 00000000000..8282b0150a4 --- /dev/null +++ b/contracts/.changeset/dull-plums-flash.md @@ -0,0 +1,9 @@ +--- +'@chainlink/contracts': minor +--- + +#internal Add new event in setRateLimitAdmin for Atlas + +PR issue: CCIP-4099 + +Solidity Review issue: CCIP-3966 \ No newline at end of file diff --git a/contracts/.changeset/eight-timers-sip.md b/contracts/.changeset/eight-timers-sip.md deleted file mode 100644 index 3f81544e34f..00000000000 --- a/contracts/.changeset/eight-timers-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Add new channel definitions config store contract for parallel compositions #added diff --git a/contracts/.changeset/eighty-ways-vanish.md b/contracts/.changeset/eighty-ways-vanish.md deleted file mode 100644 index 3a48ca4e710..00000000000 --- a/contracts/.changeset/eighty-ways-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Publish a comment in PR mentioning the actor and informing her about avilability of Slither reports. diff --git a/contracts/.changeset/empty-ants-suffer.md b/contracts/.changeset/empty-ants-suffer.md deleted file mode 100644 index 8270da10f1e..00000000000 --- a/contracts/.changeset/empty-ants-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Added updateFromPrevious method to Functions ToS contract diff --git a/contracts/.changeset/few-camels-tan.md b/contracts/.changeset/few-camels-tan.md deleted file mode 100644 index ca2574171d5..00000000000 --- a/contracts/.changeset/few-camels-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -bump dependencies diff --git a/contracts/.changeset/few-parents-punch.md b/contracts/.changeset/few-parents-punch.md deleted file mode 100644 index 88a885606bf..00000000000 --- a/contracts/.changeset/few-parents-punch.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -update comments - - -PR issue: SHIP-3557 diff --git a/contracts/.changeset/fluffy-papayas-chew.md b/contracts/.changeset/fluffy-papayas-chew.md deleted file mode 100644 index aa7b145e8f6..00000000000 --- a/contracts/.changeset/fluffy-papayas-chew.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -#internal Change Chain Reader testing contract Triggered event for easier testing of filtering by non indexed evm data. - - -PR issue: BCFR-203 diff --git a/contracts/.changeset/forty-radios-brush.md b/contracts/.changeset/forty-radios-brush.md deleted file mode 100644 index 5356ffedb1b..00000000000 --- a/contracts/.changeset/forty-radios-brush.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Add Configurator contract - - -PR issue: MERC-6185 diff --git a/contracts/.changeset/funny-meals-remember.md b/contracts/.changeset/funny-meals-remember.md deleted file mode 100644 index cb40a347e45..00000000000 --- a/contracts/.changeset/funny-meals-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -#internal Modify Contract Reader tester helper BCFR-912 diff --git a/contracts/.changeset/heavy-balloons-cheat.md b/contracts/.changeset/heavy-balloons-cheat.md deleted file mode 100644 index a6cc994c8d3..00000000000 --- a/contracts/.changeset/heavy-balloons-cheat.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#added Add ZKSync L2EP SequencerUptimeFeed contract -#added Add ZKSync L2EP Validator contract - - -PR issue: SHIP-3004 diff --git a/contracts/.changeset/hip-cherries-marry.md b/contracts/.changeset/hip-cherries-marry.md deleted file mode 100644 index d5928a5dbf2..00000000000 --- a/contracts/.changeset/hip-cherries-marry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -Add encryptionPublicKey to CapabilitiesRegistry.sol diff --git a/contracts/.changeset/itchy-deers-deny.md b/contracts/.changeset/itchy-deers-deny.md deleted file mode 100644 index 697f451cf56..00000000000 --- a/contracts/.changeset/itchy-deers-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -More comprehensive & product-scoped Solidity Foundry pipeline diff --git a/contracts/.changeset/itchy-turtles-agree.md b/contracts/.changeset/itchy-turtles-agree.md deleted file mode 100644 index 930ab850d9b..00000000000 --- a/contracts/.changeset/itchy-turtles-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -#internal Add an event with indexed topics that get hashed to Chain Reader Tester contract. diff --git a/contracts/.changeset/loud-lobsters-guess.md b/contracts/.changeset/loud-lobsters-guess.md deleted file mode 100644 index e470267e4e4..00000000000 --- a/contracts/.changeset/loud-lobsters-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -auto: create a replication from v2_3 to v2_3_zksync diff --git a/contracts/.changeset/mean-zoos-fly.md b/contracts/.changeset/mean-zoos-fly.md deleted file mode 100644 index 72eb98198d0..00000000000 --- a/contracts/.changeset/mean-zoos-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -add OZ v0.5 contracts diff --git a/contracts/.changeset/metal-ducks-hunt.md b/contracts/.changeset/metal-ducks-hunt.md new file mode 100644 index 00000000000..caba4819256 --- /dev/null +++ b/contracts/.changeset/metal-ducks-hunt.md @@ -0,0 +1,10 @@ +--- +'@chainlink/contracts': patch +--- + +#feature adds OZ AccessControl support to the registry module + + +PR issue: CCIP-4105 + +Solidity Review issue: CCIP-3966 \ No newline at end of file diff --git a/contracts/.changeset/nasty-llamas-prove.md b/contracts/.changeset/nasty-llamas-prove.md deleted file mode 100644 index dd344676808..00000000000 --- a/contracts/.changeset/nasty-llamas-prove.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -DEVSVCS-147: add a reentrancy guard for balance monitor - -PR issue: DEVSVCS-147 diff --git a/contracts/.changeset/nice-planets-share.md b/contracts/.changeset/nice-planets-share.md deleted file mode 100644 index a8af56ac613..00000000000 --- a/contracts/.changeset/nice-planets-share.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -EnumerableMap Library for an Address to Bytes mapping diff --git a/contracts/.changeset/orange-plums-fold.md b/contracts/.changeset/orange-plums-fold.md deleted file mode 100644 index b6cf88c81b3..00000000000 --- a/contracts/.changeset/orange-plums-fold.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Adding USDCReaderTester contract for CCIP integration tests #internal - - -CCIP-2881 \ No newline at end of file diff --git a/contracts/.changeset/polite-masks-jog.md b/contracts/.changeset/polite-masks-jog.md deleted file mode 100644 index 93fba83b558..00000000000 --- a/contracts/.changeset/polite-masks-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal diff --git a/contracts/.changeset/poor-ears-hear.md b/contracts/.changeset/poor-ears-hear.md new file mode 100644 index 00000000000..6e0fbe26663 --- /dev/null +++ b/contracts/.changeset/poor-ears-hear.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal split onRamp and feeQuoter tests diff --git a/contracts/.changeset/proud-pears-tie.md b/contracts/.changeset/proud-pears-tie.md deleted file mode 100644 index 93fba83b558..00000000000 --- a/contracts/.changeset/proud-pears-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal diff --git a/contracts/.changeset/quick-olives-accept.md b/contracts/.changeset/quick-olives-accept.md deleted file mode 100644 index 31c59983e47..00000000000 --- a/contracts/.changeset/quick-olives-accept.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -#updated move latest ccip contracts code from ccip repo to chainlink repo - -PR issue: CCIP-2946 diff --git a/contracts/.changeset/flat-turkeys-rule.md b/contracts/.changeset/quick-pans-tie.md similarity index 52% rename from contracts/.changeset/flat-turkeys-rule.md rename to contracts/.changeset/quick-pans-tie.md index 2dedbe653ed..5c96ea701dc 100644 --- a/contracts/.changeset/flat-turkeys-rule.md +++ b/contracts/.changeset/quick-pans-tie.md @@ -2,4 +2,4 @@ '@chainlink/contracts': patch --- -#internal merge ccip contracts +#internal Enable Solhint for tests diff --git a/contracts/.changeset/quiet-lamps-walk.md b/contracts/.changeset/quiet-lamps-walk.md deleted file mode 100644 index 93fba83b558..00000000000 --- a/contracts/.changeset/quiet-lamps-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal diff --git a/contracts/.changeset/quiet-moles-retire.md b/contracts/.changeset/quiet-moles-retire.md deleted file mode 100644 index 6351f36a16c..00000000000 --- a/contracts/.changeset/quiet-moles-retire.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Use reusable workflow for Solidity Artifacts pipeline, move some actions to chainlink-github-actions repository - - -PR issue: TT-1693 diff --git a/contracts/.changeset/rich-lamps-do.md b/contracts/.changeset/rich-lamps-do.md deleted file mode 100644 index 3f432d3de6b..00000000000 --- a/contracts/.changeset/rich-lamps-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -update zksync automation contract version and small fixes diff --git a/contracts/.changeset/seven-donkeys-live.md b/contracts/.changeset/seven-donkeys-live.md deleted file mode 100644 index 141588f5b9f..00000000000 --- a/contracts/.changeset/seven-donkeys-live.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -improve cron contracts imports diff --git a/contracts/.changeset/sharp-avocados-arrive.md b/contracts/.changeset/sharp-avocados-arrive.md deleted file mode 100644 index 6bfd0524a88..00000000000 --- a/contracts/.changeset/sharp-avocados-arrive.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal remove CCIP 1.5 - - -PR issue: CCIP-3748 \ No newline at end of file diff --git a/contracts/.changeset/silent-houses-join.md b/contracts/.changeset/silent-houses-join.md deleted file mode 100644 index 4138756c78a..00000000000 --- a/contracts/.changeset/silent-houses-join.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Enable rotating encryptionPublicKey in CapabilitiesRegistry contract diff --git a/contracts/.changeset/silver-pots-cover.md b/contracts/.changeset/silver-pots-cover.md deleted file mode 100644 index 93fba83b558..00000000000 --- a/contracts/.changeset/silver-pots-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal diff --git a/contracts/.changeset/slimy-pens-listen.md b/contracts/.changeset/slimy-pens-listen.md deleted file mode 100644 index ff81d222378..00000000000 --- a/contracts/.changeset/slimy-pens-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal prevent editing whether or not a DON accepts workflows diff --git a/contracts/.changeset/strong-boats-brake.md b/contracts/.changeset/strong-boats-brake.md deleted file mode 100644 index 4f1b780565f..00000000000 --- a/contracts/.changeset/strong-boats-brake.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -Add linkage between PR and Jira's Solidity Review issue - - -PR issue: TT-1624 diff --git a/contracts/.changeset/tall-donkeys-flow.md b/contracts/.changeset/tall-donkeys-flow.md deleted file mode 100644 index 3753880baeb..00000000000 --- a/contracts/.changeset/tall-donkeys-flow.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -#internal Updated ChainReaderTester to include dynamic and static nested structs in TestStruct - - -PR issue: BCFR-44 - -Solidity Review issue: BCFR-957 \ No newline at end of file diff --git a/contracts/.changeset/tender-comics-check.md b/contracts/.changeset/tender-comics-check.md deleted file mode 100644 index 6ea48d92e4e..00000000000 --- a/contracts/.changeset/tender-comics-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal prevent reentrancy when configuring DON in capabilities registry diff --git a/contracts/.changeset/thirty-lamps-reply.md b/contracts/.changeset/thirty-lamps-reply.md deleted file mode 100644 index d8bcf8d4e83..00000000000 --- a/contracts/.changeset/thirty-lamps-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -implement an auto registry for zksync with no forwarder interface change diff --git a/contracts/.changeset/three-stingrays-compete.md b/contracts/.changeset/three-stingrays-compete.md deleted file mode 100644 index 613b2784657..00000000000 --- a/contracts/.changeset/three-stingrays-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': minor ---- - -add ccip contracts to the repo diff --git a/contracts/.changeset/tidy-kings-itch.md b/contracts/.changeset/tidy-kings-itch.md deleted file mode 100644 index e493a6cc551..00000000000 --- a/contracts/.changeset/tidy-kings-itch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal minor keystone improvements diff --git a/contracts/.changeset/twenty-pears-battle.md b/contracts/.changeset/twenty-pears-battle.md deleted file mode 100644 index 5a204f68891..00000000000 --- a/contracts/.changeset/twenty-pears-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -update token transfer logic in weth9 diff --git a/contracts/.changeset/unlucky-rocks-marry.md b/contracts/.changeset/unlucky-rocks-marry.md deleted file mode 100644 index 723bb1e130a..00000000000 --- a/contracts/.changeset/unlucky-rocks-marry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@chainlink/contracts': patch ---- - -#internal use ERC165Checker diff --git a/contracts/.solhint-test.json b/contracts/.solhint-test.json new file mode 100644 index 00000000000..e26b18b597b --- /dev/null +++ b/contracts/.solhint-test.json @@ -0,0 +1,50 @@ +{ + "extends": "solhint:recommended", + "plugins": ["prettier", "chainlink-solidity"], + "rules": { + "compiler-version": ["off", "^0.8.0"], + "const-name-snakecase": "off", + "constructor-syntax": "error", + "var-name-mixedcase": "off", + "func-named-parameters": "off", + "immutable-vars-naming": "off", + "no-inline-assembly": "off", + "contract-name-camelcase": "off", + "one-contract-per-file": "off", + "avoid-low-level-calls": "off", + "reentrancy": "off", + "func-name-mixedcase": "off", + "no-unused-import": "error", + "gas-struct-packing": "warn", + "interface-starts-with-i": "warn", + "func-visibility": [ + "error", + { + "ignoreConstructors": true + } + ], + "not-rely-on-time": "off", + "prettier/prettier": [ + "off", + { + "endOfLine": "auto" + } + ], + "no-empty-blocks": "off", + "quotes": ["error", "double"], + "reason-string": [ + "warn", + { + "maxLength": 64 + } + ], + "chainlink-solidity/prefix-internal-functions-with-underscore": "warn", + "chainlink-solidity/prefix-private-functions-with-underscore": "warn", + "chainlink-solidity/prefix-storage-variables-with-s-underscore": "warn", + "chainlink-solidity/prefix-immutable-variables-with-i": "warn", + "chainlink-solidity/all-caps-constant-storage-variables": "warn", + "chainlink-solidity/no-hardhat-imports": "warn", + "chainlink-solidity/inherited-constructor-args-not-in-contract-definition": "warn", + "chainlink-solidity/explicit-returns": "warn" + } +} diff --git a/contracts/.solhintignore b/contracts/.solhintignore index 81291fe0871..7ae5b10d150 100644 --- a/contracts/.solhintignore +++ b/contracts/.solhintignore @@ -1,3 +1,6 @@ +# Test files run with a different solhint ruleset, ignore them here. +./**/*.t.sol + # Ignore frozen Automation code ./src/v0.8/automation/v1_2 ./src/v0.8/automation/interfaces/v1_2 @@ -29,13 +32,11 @@ # Ignore Functions v1.0.0 code that was frozen after audit ./src/v0.8/functions/v1_0_0 -# Ignore tests, this should not be the long term plan but is OK in the short term -./src/v0.8/**/*.t.sol -./src/v0.8/mocks -./src/v0.8/tests -./src/v0.8/llo-feeds/test +# Test helpers ./src/v0.8/vrf/testhelpers +./src/v0.8/tests ./src/v0.8/functions/tests +./src/v0.8/mocks/ # Always ignore vendor ./src/v0.8/vendor diff --git a/contracts/.solhintignore-test b/contracts/.solhintignore-test new file mode 100644 index 00000000000..acaca4fe1e4 --- /dev/null +++ b/contracts/.solhintignore-test @@ -0,0 +1,26 @@ +./src/v0.8/automation/ +./src/v0.8/vrf/ +./src/v0.8/mocks +./src/v0.8/tests +./src/v0.8/llo-feeds/ +./src/v0.8/automation/ +./src/v0.8/transmission/ +./src/v0.8/l2ep/ +./src/v0.8/shared/ +./src/v0.8/operatorforwarder/ +./src/v0.8/functions/ +./src/v0.8/liquiditymanager/ +./src/v0.8/keystone/ +./src/v0.8/llo-feeds/ + + +# Ignore Functions v1.0.0 code that was frozen after audit +./src/v0.8/functions/v1_0_0 + + +# Always ignore vendor +./src/v0.8/vendor +./node_modules/ + +# Ignore tweaked vendored contracts +./src/v0.8/shared/enumerable/EnumerableSetWithBytes16.sol \ No newline at end of file diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md index 01a9dd4b6c4..5e2b0ee73a0 100644 --- a/contracts/CHANGELOG.md +++ b/contracts/CHANGELOG.md @@ -1,5 +1,52 @@ # @chainlink/contracts +## 1.3.0 - 2024-10-21 + +### Minor Changes + +- [#14207](https://github.com/smartcontractkit/chainlink/pull/14207) [`328b62a`](https://github.com/smartcontractkit/chainlink/commit/328b62ae5067619e59da42f6db6703d3b327f1a2) Thanks [@ilija42](https://github.com/ilija42)! - #internal Change Chain Reader testing contract Triggered event for easier testing of filtering by non indexed evm data. +- [#14622](https://github.com/smartcontractkit/chainlink/pull/14622) [`c654322`](https://github.com/smartcontractkit/chainlink/commit/c654322acea7da8e6bd84a8a045690002f1f172d) Thanks [@ilija42](https://github.com/ilija42)! - #internal Modify Contract Reader tester helper BCFR-912 +- [#14709](https://github.com/smartcontractkit/chainlink/pull/14709) [`1560aa9`](https://github.com/smartcontractkit/chainlink/commit/1560aa9167a812abe3a8370c033b3290dcbcb261) Thanks [@KuphJr](https://github.com/KuphJr)! - Add encryptionPublicKey to CapabilitiesRegistry.sol +- [#14016](https://github.com/smartcontractkit/chainlink/pull/14016) [`8b9f2b6`](https://github.com/smartcontractkit/chainlink/commit/8b9f2b6b9098e8ec2368773368239106d066e4e3) Thanks [@ilija42](https://github.com/ilija42)! - #internal Add an event with indexed topics that get hashed to Chain Reader Tester contract. +- [#14012](https://github.com/smartcontractkit/chainlink/pull/14012) [`518cc28`](https://github.com/smartcontractkit/chainlink/commit/518cc281b14727c26cd7fcb9db882b45837d443a) Thanks [@defistar](https://github.com/defistar)! - EnumerableMap Library for an Address to Bytes mapping +- [#14266](https://github.com/smartcontractkit/chainlink/pull/14266) [`c323e0d`](https://github.com/smartcontractkit/chainlink/commit/c323e0d600c659a4ea584dbae0a0db187afd51eb) Thanks [@asoliman92](https://github.com/asoliman92)! - #updated move latest ccip contracts code from ccip repo to chainlink repo +- [#14511](https://github.com/smartcontractkit/chainlink/pull/14511) [`8fa9a67`](https://github.com/smartcontractkit/chainlink/commit/8fa9a67dfc130feab290860f0b7bf860ddc86bb3) Thanks [@silaslenihan](https://github.com/silaslenihan)! - #internal Updated ChainReaderTester to include dynamic and static nested structs in TestStruct +- [#13941](https://github.com/smartcontractkit/chainlink/pull/13941) [`9e74eee`](https://github.com/smartcontractkit/chainlink/commit/9e74eee9d415b386db33bdf2dd44facc82cd3551) Thanks [@RensR](https://github.com/RensR)! - add ccip contracts to the repo + +### Patch Changes + +- [#13937](https://github.com/smartcontractkit/chainlink/pull/13937) [`27d9c71`](https://github.com/smartcontractkit/chainlink/commit/27d9c71b196961666de87bc3128d31f3c22fb3fa) Thanks [@cds95](https://github.com/cds95)! - #internal address security vulnerabilities around updating nodes and node operators on capabilities registry +- [#14241](https://github.com/smartcontractkit/chainlink/pull/14241) [`7c248e7`](https://github.com/smartcontractkit/chainlink/commit/7c248e7c466ad278b0024e4ac743813009b16805) Thanks [@cds95](https://github.com/cds95)! - #internal index don ID in ConfigSet event +- [#13780](https://github.com/smartcontractkit/chainlink/pull/13780) [`af335c1`](https://github.com/smartcontractkit/chainlink/commit/af335c1a522769c8c29858d8d6510330af3204cf) Thanks [@samsondav](https://github.com/samsondav)! - Add new channel definitions config store contract for parallel compositions #added +- [#14198](https://github.com/smartcontractkit/chainlink/pull/14198) [`e452ee1`](https://github.com/smartcontractkit/chainlink/commit/e452ee1ddb520b86f827ac75cccdb0719e9f5335) Thanks [@Tofel](https://github.com/Tofel)! - Publish a comment in PR mentioning the actor and informing her about avilability of Slither reports. +- [#13795](https://github.com/smartcontractkit/chainlink/pull/13795) [`683a12e`](https://github.com/smartcontractkit/chainlink/commit/683a12e85e91628f240fe24f32b982b53ac30bd9) Thanks [@KuphJr](https://github.com/KuphJr)! - Added updateFromPrevious method to Functions ToS contract +- [#14093](https://github.com/smartcontractkit/chainlink/pull/14093) [`95ae744`](https://github.com/smartcontractkit/chainlink/commit/95ae74437c42699d27e1d37f66ca8ddef68ce58f) Thanks [@RensR](https://github.com/RensR)! - bump dependencies +- [#14371](https://github.com/smartcontractkit/chainlink/pull/14371) [`0efcf38`](https://github.com/smartcontractkit/chainlink/commit/0efcf380192837a64bbca946474866e8e1bdcec0) Thanks [@jlaveracll](https://github.com/jlaveracll)! - update comments +- [#14345](https://github.com/smartcontractkit/chainlink/pull/14345) [`c83c687`](https://github.com/smartcontractkit/chainlink/commit/c83c68735bdee6bbd8510733b7415797cd08ecbd) Thanks [@makramkd](https://github.com/makramkd)! - #internal merge ccip contracts +- [#14249](https://github.com/smartcontractkit/chainlink/pull/14249) [`e8c2453`](https://github.com/smartcontractkit/chainlink/commit/e8c2453e8581ed7ad83033d938567dcce8f6c5a5) Thanks [@samsondav](https://github.com/samsondav)! - Add Configurator contract +- [#14245](https://github.com/smartcontractkit/chainlink/pull/14245) [`39a6f91`](https://github.com/smartcontractkit/chainlink/commit/39a6f91fcaba4c51e41a33afc3cdb572af8343dd) Thanks [@jlaveracll](https://github.com/jlaveracll)! - #added Add ZKSync L2EP SequencerUptimeFeed contract #added Add ZKSync L2EP Validator contract +- [#13986](https://github.com/smartcontractkit/chainlink/pull/13986) [`4b91691`](https://github.com/smartcontractkit/chainlink/commit/4b9169131cd44d6cb4f00dae2b33e49626af5f7d) Thanks [@Tofel](https://github.com/Tofel)! - More comprehensive & product-scoped Solidity Foundry pipeline +- [#14035](https://github.com/smartcontractkit/chainlink/pull/14035) [`215277f`](https://github.com/smartcontractkit/chainlink/commit/215277f9e041d18dc5686c697e6959d5edaaf346) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - auto: create a replication from v2_3 to v2_3_zksync +- [#14065](https://github.com/smartcontractkit/chainlink/pull/14065) [`499a677`](https://github.com/smartcontractkit/chainlink/commit/499a67705ac7ea525685c4a064ff4aa52b08fa44) Thanks [@RyanRHall](https://github.com/RyanRHall)! - add OZ v0.5 contracts +- [#14108](https://github.com/smartcontractkit/chainlink/pull/14108) [`08194be`](https://github.com/smartcontractkit/chainlink/commit/08194beb46355135dedde89d37838f5da36f2cef) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - DEVSVCS-147: add a reentrancy guard for balance monitor +- [#14516](https://github.com/smartcontractkit/chainlink/pull/14516) [`0e32c07`](https://github.com/smartcontractkit/chainlink/commit/0e32c07d22973343e722a228ff1c3b1e8f9bc04e) Thanks [@mateusz-sekara](https://github.com/mateusz-sekara)! - Adding USDCReaderTester contract for CCIP integration tests #internal +- [#14017](https://github.com/smartcontractkit/chainlink/pull/14017) [`1257d33`](https://github.com/smartcontractkit/chainlink/commit/1257d33913d243c146bccbf4bda67a2bb1c7d5af) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal +- [#14543](https://github.com/smartcontractkit/chainlink/pull/14543) [`c4fa565`](https://github.com/smartcontractkit/chainlink/commit/c4fa565f5441bfa997907256e1990f9be276934d) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal +- [#14350](https://github.com/smartcontractkit/chainlink/pull/14350) [`070b272`](https://github.com/smartcontractkit/chainlink/commit/070b272f30054be6d4239d078121ca3b3054fc33) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal +- [#14436](https://github.com/smartcontractkit/chainlink/pull/14436) [`f74ac81`](https://github.com/smartcontractkit/chainlink/commit/f74ac81d5db7a89b04252938f4b5ff34e3f7bbbe) Thanks [@Tofel](https://github.com/Tofel)! - Use reusable workflow for Solidity Artifacts pipeline, move some actions to chainlink-github-actions repository +- [#14390](https://github.com/smartcontractkit/chainlink/pull/14390) [`f202bcf`](https://github.com/smartcontractkit/chainlink/commit/f202bcfe45648cc803b38650a7aaf6fecb91969d) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - update zksync automation contract version and small fixes +- [#13927](https://github.com/smartcontractkit/chainlink/pull/13927) [`ce90bc3`](https://github.com/smartcontractkit/chainlink/commit/ce90bc32f562e92af3d22c895446a963109c36e3) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - improve cron contracts imports +- [#14739](https://github.com/smartcontractkit/chainlink/pull/14739) [`4842271`](https://github.com/smartcontractkit/chainlink/commit/4842271b0f7054f5f1364c59d3d9da534c5d4f25) Thanks [@RensR](https://github.com/RensR)! - #internal remove CCIP 1.5 +- [#14760](https://github.com/smartcontractkit/chainlink/pull/14760) [`3af39c8`](https://github.com/smartcontractkit/chainlink/commit/3af39c803201461009ef63f709851fe6a24f0284) Thanks [@KuphJr](https://github.com/KuphJr)! - Enable rotating encryptionPublicKey in CapabilitiesRegistry contract +- [#13993](https://github.com/smartcontractkit/chainlink/pull/13993) [`f5e0bd6`](https://github.com/smartcontractkit/chainlink/commit/f5e0bd614a6c42d195c4ad74a10f7070970d01d5) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal +- [#14092](https://github.com/smartcontractkit/chainlink/pull/14092) [`3399dd6`](https://github.com/smartcontractkit/chainlink/commit/3399dd6d7fee12bd8d099b74397edcc4dc56c11d) Thanks [@cds95](https://github.com/cds95)! - #internal prevent editing whether or not a DON accepts workflows +- [#14521](https://github.com/smartcontractkit/chainlink/pull/14521) [`b4360c9`](https://github.com/smartcontractkit/chainlink/commit/b4360c9aece538e20ef688750adcd5a838729930) Thanks [@Tofel](https://github.com/Tofel)! - Add linkage between PR and Jira's Solidity Review issue +- [#13970](https://github.com/smartcontractkit/chainlink/pull/13970) [`cefbb09`](https://github.com/smartcontractkit/chainlink/commit/cefbb09797249309ac18e4ef81147e30f7c24360) Thanks [@cds95](https://github.com/cds95)! - #internal prevent reentrancy when configuring DON in capabilities registry +- [#14037](https://github.com/smartcontractkit/chainlink/pull/14037) [`9c240b6`](https://github.com/smartcontractkit/chainlink/commit/9c240b686753c72f94f8fb7e8c636483d5759963) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - implement an auto registry for zksync with no forwarder interface change +- [#14767](https://github.com/smartcontractkit/chainlink/pull/14767) [`a3b552f`](https://github.com/smartcontractkit/chainlink/commit/a3b552f0f87546c5250b544b5dd2a4d31b7a9b42) Thanks [@RensR](https://github.com/RensR)! - #internal minor keystone improvements +- [#14481](https://github.com/smartcontractkit/chainlink/pull/14481) [`1a5e591`](https://github.com/smartcontractkit/chainlink/commit/1a5e591875d5d478be65605ad5dc66e8bf8b915b) Thanks [@FelixFan1992](https://github.com/FelixFan1992)! - update token transfer logic in weth9 +- [#14231](https://github.com/smartcontractkit/chainlink/pull/14231) [`7a41ae7`](https://github.com/smartcontractkit/chainlink/commit/7a41ae73bcb5f5eb9ffbc4f25059dbbc236a7e8a) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal use ERC165Checker + ## 1.2.0 - 2024-07-18 ### Minor Changes diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 39bd88c732c..4182f6597fe 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -7,7 +7,7 @@ ARMProxyTest:test_ARMIsCursed_Success() (gas: 47082) BurnFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28962) BurnFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55341) BurnFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244152) -BurnFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24209) +BurnFromMintTokenPool_lockOrBurn:test_setup_Success() (gas: 24187) BurnMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 27681) BurnMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55341) BurnMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 242036) @@ -67,8 +67,8 @@ CCIPHome_setCandidate:test_setCandidate_CanOnlySelfCall_reverts() (gas: 29383) CCIPHome_setCandidate:test_setCandidate_ConfigDigestMismatch_reverts() (gas: 1395154) CCIPHome_setCandidate:test_setCandidate_success() (gas: 1365439) DefensiveExampleTest:test_HappyPath_Success() (gas: 200473) -DefensiveExampleTest:test_Recovery() (gas: 424859) -E2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1520821) +DefensiveExampleTest:test_Recovery() (gas: 424876) +E2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1519829) EtherSenderReceiverTest_ccipReceive:test_ccipReceive_fallbackToWethTransfer() (gas: 96962) EtherSenderReceiverTest_ccipReceive:test_ccipReceive_happyPath() (gas: 49812) EtherSenderReceiverTest_ccipReceive:test_ccipReceive_wrongToken() (gas: 17457) @@ -152,10 +152,10 @@ FeeQuoter_getTokenAndGasPrices:test_ZeroGasPrice_Success() (gas: 109131) FeeQuoter_getTokenPrice:test_GetTokenPriceFromFeed_Success() (gas: 68015) FeeQuoter_getTokenPrice:test_GetTokenPrice_LocalMoreRecent_Success() (gas: 33463) FeeQuoter_getTokenPrices:test_GetTokenPrices_Success() (gas: 78498) -FeeQuoter_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 39502) +FeeQuoter_getTokenTransferCost:test_CustomTokenBpsFee_Success() (gas: 37372) FeeQuoter_getTokenTransferCost:test_FeeTokenBpsFee_Success() (gas: 35151) FeeQuoter_getTokenTransferCost:test_LargeTokenTransferChargesMaxFeeAndGas_Success() (gas: 28241) -FeeQuoter_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 98330) +FeeQuoter_getTokenTransferCost:test_MixedTokenTransferFee_Success() (gas: 96218) FeeQuoter_getTokenTransferCost:test_NoTokenTransferChargesZeroFee_Success() (gas: 20702) FeeQuoter_getTokenTransferCost:test_SmallTokenTransferChargesMinFeeAndGas_Success() (gas: 28049) FeeQuoter_getTokenTransferCost:test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() (gas: 28072) @@ -227,43 +227,20 @@ FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddressPrecompiles_Revert() ( FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddress_Revert() (gas: 10884) FeeQuoter_validateDestFamilyAddress:test_ValidEVMAddress_Success() (gas: 6819) FeeQuoter_validateDestFamilyAddress:test_ValidNonEVMAddress_Success() (gas: 6545) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209719) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 136083) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109924) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147189) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217255) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 426523) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 269168) -HybridUSDCTokenPoolMigrationTests:test_ProposeMigration_ChainNotUsingLockRelease_Revert() (gas: 15876) -HybridUSDCTokenPoolMigrationTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 113657) -HybridUSDCTokenPoolMigrationTests:test_burnLockedUSDC_invalidPermissions_Revert() (gas: 39333) -HybridUSDCTokenPoolMigrationTests:test_cancelExistingCCTPMigrationProposal() (gas: 56302) -HybridUSDCTokenPoolMigrationTests:test_cannotCancelANonExistentMigrationProposal() (gas: 12758) -HybridUSDCTokenPoolMigrationTests:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 13423) -HybridUSDCTokenPoolMigrationTests:test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() (gas: 67449) -HybridUSDCTokenPoolMigrationTests:test_cannotRevertChainMechanism_afterMigration_Revert() (gas: 313663) -HybridUSDCTokenPoolMigrationTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 177187) -HybridUSDCTokenPoolMigrationTests:test_cnanotProvideLiquidity_AfterMigration_Revert() (gas: 314046) -HybridUSDCTokenPoolMigrationTests:test_excludeTokensWhenNoMigrationProposalPending_Revert() (gas: 13712) -HybridUSDCTokenPoolMigrationTests:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 310162) -HybridUSDCTokenPoolMigrationTests:test_transferLiquidity_Success() (gas: 167156) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_destChain_Success() (gas: 156334) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 516342) -HybridUSDCTokenPoolTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209569) -HybridUSDCTokenPoolTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 136048) -HybridUSDCTokenPoolTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109914) -HybridUSDCTokenPoolTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147177) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217112) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 426248) -HybridUSDCTokenPoolTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 269124) -HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 113635) -HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 177099) -HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 167068) -LockReleaseTokenPool_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 2778635) +HybridLockReleaseUSDCTokenPool_TransferLiquidity:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 176969) +HybridLockReleaseUSDCTokenPool_TransferLiquidity:test_transferLiquidity_Success() (gas: 167002) +HybridLockReleaseUSDCTokenPool_lockOrBurn:test_PrimaryMechanism_Success() (gas: 135921) +HybridLockReleaseUSDCTokenPool_lockOrBurn:test_WhileMigrationPause_Revert() (gas: 109740) +HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism_Success() (gas: 147013) +HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism_thenswitchToPrimary_Success() (gas: 209245) +HybridLockReleaseUSDCTokenPool_releaseOrMint:test_OnLockReleaseMechanism_Success() (gas: 216909) +HybridLockReleaseUSDCTokenPool_releaseOrMint:test_WhileMigrationPause_Revert() (gas: 113472) +HybridLockReleaseUSDCTokenPool_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 268981) +LockReleaseTokenPool_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 2788658) LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 30110) LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Success() (gas: 80282) LockReleaseTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 59690) -LockReleaseTokenPool_provideLiquidity:test_LiquidityNotAccepted_Revert() (gas: 2775052) +LockReleaseTokenPool_provideLiquidity:test_LiquidityNotAccepted_Revert() (gas: 2785075) LockReleaseTokenPool_provideLiquidity:test_Unauthorized_Revert() (gas: 11489) LockReleaseTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 72956) LockReleaseTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 56476) @@ -275,8 +252,6 @@ LockReleaseTokenPool_transferLiquidity:test_transferLiquidity_Success() (gas: 83 LockReleaseTokenPool_transferLiquidity:test_transferLiquidity_transferTooMuch_Revert() (gas: 56013) LockReleaseTokenPool_withdrawalLiquidity:test_InsufficientLiquidity_Revert() (gas: 60164) LockReleaseTokenPool_withdrawalLiquidity:test_Unauthorized_Revert() (gas: 11464) -LockRelease_setRateLimitAdmin:test_SetRateLimitAdmin_Revert() (gas: 11024) -LockRelease_setRateLimitAdmin:test_SetRateLimitAdmin_Success() (gas: 35126) MerkleMultiProofTest:test_CVE_2023_34459() (gas: 5500) MerkleMultiProofTest:test_EmptyLeaf_Revert() (gas: 3607) MerkleMultiProofTest:test_MerkleRoot256() (gas: 394891) @@ -308,24 +283,24 @@ MultiAggregateRateLimiter_getTokenBucket:test_TimeUnderflow_Revert() (gas: 15907 MultiAggregateRateLimiter_getTokenValue:test_GetTokenValue_Success() (gas: 17602) MultiAggregateRateLimiter_getTokenValue:test_NoTokenPrice_Reverts() (gas: 21630) MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageFromUnauthorizedCaller_Revert() (gas: 14636) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 210589) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 58469) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 17809) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitDisabled_Success() (gas: 45220) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitExceeded_Revert() (gas: 46488) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitReset_Success() (gas: 76929) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 308969) -MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokens_Success() (gas: 50654) -MultiAggregateRateLimiter_onOutboundMessage:test_RateLimitValueDifferentLanes_Success() (gas: 51305) -MultiAggregateRateLimiter_onOutboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 19393) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 210571) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 58451) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 17791) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitDisabled_Success() (gas: 45202) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitExceeded_Revert() (gas: 46470) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitReset_Success() (gas: 76911) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 308951) +MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokens_Success() (gas: 50636) +MultiAggregateRateLimiter_onOutboundMessage:test_RateLimitValueDifferentLanes_Success() (gas: 51287) +MultiAggregateRateLimiter_onOutboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 19375) MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageFromUnauthorizedCaller_Revert() (gas: 15914) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 210309) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 60262) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitDisabled_Success() (gas: 47043) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitExceeded_Revert() (gas: 48279) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitReset_Success() (gas: 77936) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 308915) -MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokens_Success() (gas: 52424) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 210291) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDisabledRateLimitToken_Success() (gas: 60244) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitDisabled_Success() (gas: 47025) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitExceeded_Revert() (gas: 48261) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithRateLimitReset_Success() (gas: 77918) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 308897) +MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithTokens_Success() (gas: 52406) MultiAggregateRateLimiter_setFeeQuoter:test_OnlyOwner_Revert() (gas: 10967) MultiAggregateRateLimiter_setFeeQuoter:test_Owner_Success() (gas: 19190) MultiAggregateRateLimiter_setFeeQuoter:test_ZeroAddress_Revert() (gas: 10642) @@ -374,213 +349,213 @@ NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23706) NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38778) NonceManager_NonceIncrementation:test_incrementNoncesInboundAndOutbound_Success() (gas: 71901) -NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 185976) -NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 189423) -NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 252593) -NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 220830) -NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 60591) -NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 153010) +NonceManager_OffRampUpgrade:test_NoPrevOffRampForChain_Success() (gas: 185739) +NonceManager_OffRampUpgrade:test_UpgradedNonceNewSenderStartsAtZero_Success() (gas: 189192) +NonceManager_OffRampUpgrade:test_UpgradedNonceStartsAtV1Nonce_Success() (gas: 252176) +NonceManager_OffRampUpgrade:test_UpgradedOffRampNonceSkipsIfMsgInFlight_Success() (gas: 220541) +NonceManager_OffRampUpgrade:test_UpgradedSenderNoncesReadsPreviousRamp_Success() (gas: 60497) +NonceManager_OffRampUpgrade:test_Upgraded_Success() (gas: 152904) NonceManager_OnRampUpgrade:test_UpgradeNonceNewSenderStartsAtZero_Success() (gas: 166101) -NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 195806) +NonceManager_OnRampUpgrade:test_UpgradeNonceStartsAtV1Nonce_Success() (gas: 195828) NonceManager_OnRampUpgrade:test_UpgradeSenderNoncesReadsPreviousRamp_Success() (gas: 139098) -NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 105257) +NonceManager_OnRampUpgrade:test_Upgrade_Success() (gas: 105168) NonceManager_applyPreviousRampsUpdates:test_MultipleRampsUpdates_success() (gas: 123604) NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOffRamp_Revert() (gas: 43403) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64775) -NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 43201) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRampAndOffRamp_Revert() (gas: 64752) +NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySetOnRamp_Revert() (gas: 43245) NonceManager_applyPreviousRampsUpdates:test_PreviousRampAlreadySet_overrideAllowed_success() (gas: 45941) NonceManager_applyPreviousRampsUpdates:test_SingleRampUpdate_success() (gas: 66889) NonceManager_applyPreviousRampsUpdates:test_ZeroInput_success() (gas: 12213) NonceManager_typeAndVersion:test_typeAndVersion() (gas: 9705) -OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 5880084) +OffRamp_afterOC3ConfigSet:test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() (gas: 5880050) OffRamp_applySourceChainConfigUpdates:test_AddMultipleChains_Success() (gas: 626115) -OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 166515) -OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 16763) -OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 274803) -OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 168560) +OffRamp_applySourceChainConfigUpdates:test_AddNewChain_Success() (gas: 166493) +OffRamp_applySourceChainConfigUpdates:test_ApplyZeroUpdates_Success() (gas: 16741) +OffRamp_applySourceChainConfigUpdates:test_InvalidOnRampUpdate_Revert() (gas: 274735) +OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChainOnRamp_Success() (gas: 168604) OffRamp_applySourceChainConfigUpdates:test_ReplaceExistingChain_Success() (gas: 181059) OffRamp_applySourceChainConfigUpdates:test_RouterAddress_Revert() (gas: 13463) OffRamp_applySourceChainConfigUpdates:test_ZeroOnRampAddress_Revert() (gas: 72746) OffRamp_applySourceChainConfigUpdates:test_ZeroSourceChainSelector_Revert() (gas: 15476) -OffRamp_applySourceChainConfigUpdates:test_allowNonOnRampUpdateAfterLaneIsUsed_success() (gas: 285153) -OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 177564) -OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 333809) -OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 277075) -OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 168494) +OffRamp_applySourceChainConfigUpdates:test_allowNonOnRampUpdateAfterLaneIsUsed_success() (gas: 285063) +OffRamp_batchExecute:test_MultipleReportsDifferentChainsSkipCursedChain_Success() (gas: 177349) +OffRamp_batchExecute:test_MultipleReportsDifferentChains_Success() (gas: 333175) +OffRamp_batchExecute:test_MultipleReportsSameChain_Success() (gas: 276441) +OffRamp_batchExecute:test_MultipleReportsSkipDuplicate_Success() (gas: 168334) OffRamp_batchExecute:test_OutOfBoundsGasLimitsAccess_Revert() (gas: 187853) -OffRamp_batchExecute:test_SingleReport_Success() (gas: 156555) -OffRamp_batchExecute:test_Unhealthy_Success() (gas: 553993) +OffRamp_batchExecute:test_SingleReport_Success() (gas: 156369) +OffRamp_batchExecute:test_Unhealthy_Success() (gas: 553439) OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10600) -OffRamp_ccipReceive:test_Reverts() (gas: 15407) -OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 92834) -OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 63500) -OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 70146) -OffRamp_commit:test_InvalidInterval_Revert() (gas: 66209) -OffRamp_commit:test_InvalidRootRevert() (gas: 65304) -OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6641216) -OffRamp_commit:test_NoConfig_Revert() (gas: 6224546) -OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 112980) -OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 121333) -OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 112979) -OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 355372) -OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 164388) -OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 141416) -OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 148426) -OffRamp_commit:test_RootWithRMNDisabled_success() (gas: 154111) -OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 61771) -OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 232626) -OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 125320) -OffRamp_commit:test_Unhealthy_Revert() (gas: 60572) -OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 207009) -OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 53689) -OffRamp_constructor:test_Constructor_Success() (gas: 6186775) -OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 136553) -OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 103634) +OffRamp_ccipReceive:test_RevertWhen_Always() (gas: 9303) +OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 92744) +OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 63432) +OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 69993) +OffRamp_commit:test_InvalidInterval_Revert() (gas: 66119) +OffRamp_commit:test_InvalidRootRevert() (gas: 65214) +OffRamp_commit:test_NoConfigWithOtherConfigPresent_Revert() (gas: 6641148) +OffRamp_commit:test_NoConfig_Revert() (gas: 6224566) +OffRamp_commit:test_OnlyGasPriceUpdates_Success() (gas: 112985) +OffRamp_commit:test_OnlyPriceUpdateStaleReport_Revert() (gas: 121175) +OffRamp_commit:test_OnlyTokenPriceUpdates_Success() (gas: 112917) +OffRamp_commit:test_PriceSequenceNumberCleared_Success() (gas: 355254) +OffRamp_commit:test_ReportAndPriceUpdate_Success() (gas: 164263) +OffRamp_commit:test_ReportOnlyRootSuccess_gas() (gas: 141269) +OffRamp_commit:test_RootAlreadyCommitted_Revert() (gas: 148268) +OffRamp_commit:test_RootWithRMNDisabled_success() (gas: 153986) +OffRamp_commit:test_SourceChainNotEnabled_Revert() (gas: 61681) +OffRamp_commit:test_StaleReportWithRoot_Success() (gas: 232354) +OffRamp_commit:test_UnauthorizedTransmitter_Revert() (gas: 125230) +OffRamp_commit:test_Unhealthy_Revert() (gas: 60482) +OffRamp_commit:test_ValidPriceUpdateThenStaleReportWithRoot_Success() (gas: 206800) +OffRamp_commit:test_ZeroEpochAndRound_Revert() (gas: 53621) +OffRamp_constructor:test_Constructor_Success() (gas: 6186663) +OffRamp_constructor:test_SourceChainSelector_Revert() (gas: 136575) +OffRamp_constructor:test_ZeroChainSelector_Revert() (gas: 103612) OffRamp_constructor:test_ZeroNonceManager_Revert() (gas: 101461) OffRamp_constructor:test_ZeroOnRampAddress_Revert() (gas: 162055) OffRamp_constructor:test_ZeroRMNRemote_Revert() (gas: 101378) -OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 101427) +OffRamp_constructor:test_ZeroTokenAdminRegistry_Revert() (gas: 101382) OffRamp_execute:test_IncorrectArrayType_Revert() (gas: 17639) -OffRamp_execute:test_LargeBatch_Success() (gas: 3406667) -OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 371505) -OffRamp_execute:test_MultipleReports_Success() (gas: 299194) -OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 7049301) +OffRamp_execute:test_LargeBatch_Success() (gas: 3374933) +OffRamp_execute:test_MultipleReportsWithPartialValidationFailures_Success() (gas: 371025) +OffRamp_execute:test_MultipleReports_Success() (gas: 298564) +OffRamp_execute:test_NoConfigWithOtherConfigPresent_Revert() (gas: 7049279) OffRamp_execute:test_NoConfig_Revert() (gas: 6273749) OffRamp_execute:test_NonArray_Revert() (gas: 27643) -OffRamp_execute:test_SingleReport_Success() (gas: 175809) -OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 147805) +OffRamp_execute:test_SingleReport_Success() (gas: 175627) +OffRamp_execute:test_UnauthorizedTransmitter_Revert() (gas: 147783) OffRamp_execute:test_WrongConfigWithSigners_Revert() (gas: 6940958) -OffRamp_execute:test_ZeroReports_Revert() (gas: 17317) -OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18537) -OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 244193) -OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 20389) -OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 205666) -OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 48884) -OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 56065) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 212828) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 85455) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 274305) -OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 91944) -OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 28658) +OffRamp_execute:test_ZeroReports_Revert() (gas: 17361) +OffRamp_executeSingleMessage:test_MessageSender_Revert() (gas: 18533) +OffRamp_executeSingleMessage:test_NonContractWithTokens_Success() (gas: 244171) +OffRamp_executeSingleMessage:test_NonContract_Success() (gas: 20363) +OffRamp_executeSingleMessage:test_TokenHandlingError_Revert() (gas: 205647) +OffRamp_executeSingleMessage:test_ZeroGasDONExecution_Revert() (gas: 48880) +OffRamp_executeSingleMessage:test_executeSingleMessage_NoTokens_Success() (gas: 56102) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() (gas: 212824) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithFailingValidation_Revert() (gas: 85495) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithTokens_Success() (gas: 274279) +OffRamp_executeSingleMessage:test_executeSingleMessage_WithVInterception_Success() (gas: 91918) +OffRamp_executeSingleReport:test_DisabledSourceChain_Revert() (gas: 28636) OffRamp_executeSingleReport:test_EmptyReport_Revert() (gas: 15580) -OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 481795) -OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 48273) -OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 34100) +OffRamp_executeSingleReport:test_InvalidSourcePoolAddress_Success() (gas: 481411) +OffRamp_executeSingleReport:test_ManualExecutionNotYetEnabled_Revert() (gas: 48295) +OffRamp_executeSingleReport:test_MismatchingDestChainSelector_Revert() (gas: 34188) OffRamp_executeSingleReport:test_NonExistingSourceChain_Revert() (gas: 28823) -OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 187698) -OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 197829) -OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 40686) -OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 404997) -OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 248698) -OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 192576) -OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 212587) -OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 243705) -OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 141547) -OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 408961) +OffRamp_executeSingleReport:test_ReceiverError_Success() (gas: 187522) +OffRamp_executeSingleReport:test_RetryFailedMessageWithoutManualExecution_Revert() (gas: 197799) +OffRamp_executeSingleReport:test_RootNotCommitted_Revert() (gas: 40664) +OffRamp_executeSingleReport:test_RouterYULCall_Revert() (gas: 404911) +OffRamp_executeSingleReport:test_SingleMessageNoTokensOtherChain_Success() (gas: 248582) +OffRamp_executeSingleReport:test_SingleMessageNoTokensUnordered_Success() (gas: 192204) +OffRamp_executeSingleReport:test_SingleMessageNoTokens_Success() (gas: 212228) +OffRamp_executeSingleReport:test_SingleMessageToNonCCIPReceiver_Success() (gas: 243641) +OffRamp_executeSingleReport:test_SingleMessagesNoTokensSuccess_gas() (gas: 141397) +OffRamp_executeSingleReport:test_SkippedIncorrectNonceStillExecutes_Success() (gas: 408631) OffRamp_executeSingleReport:test_SkippedIncorrectNonce_Success() (gas: 58241) -OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 73808) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 583208) -OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 531794) -OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 26774) -OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 549633) -OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 549580) -OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 460249) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 135267) -OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 164910) -OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3911118) -OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 121222) -OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 89706) +OffRamp_executeSingleReport:test_TokenDataMismatch_Revert() (gas: 73786) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensAndGE_Success() (gas: 582446) +OffRamp_executeSingleReport:test_TwoMessagesWithTokensSuccess_gas() (gas: 531112) +OffRamp_executeSingleReport:test_UnexpectedTokenData_Revert() (gas: 26751) +OffRamp_executeSingleReport:test_UnhealthySingleChainCurse_Revert() (gas: 549057) +OffRamp_executeSingleReport:test_Unhealthy_Success() (gas: 549093) +OffRamp_executeSingleReport:test_WithCurseOnAnotherSourceChain_Success() (gas: 460204) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessageUnordered_Success() (gas: 135139) +OffRamp_executeSingleReport:test__execute_SkippedAlreadyExecutedMessage_Success() (gas: 164782) +OffRamp_getExecutionState:test_FillExecutionState_Success() (gas: 3888824) +OffRamp_getExecutionState:test_GetDifferentChainExecutionState_Success() (gas: 121048) +OffRamp_getExecutionState:test_GetExecutionState_Success() (gas: 89561) OffRamp_manuallyExecute:test_ManualExecGasLimitMismatchSingleReport_Revert() (gas: 81178) OffRamp_manuallyExecute:test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() (gas: 74108) -OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 172634) +OffRamp_manuallyExecute:test_manuallyExecute_DoesNotRevertIfUntouched_Success() (gas: 172480) OffRamp_manuallyExecute:test_manuallyExecute_FailedTx_Revert() (gas: 212935) OffRamp_manuallyExecute:test_manuallyExecute_ForkedChain_Revert() (gas: 27166) OffRamp_manuallyExecute:test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() (gas: 164939) OffRamp_manuallyExecute:test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() (gas: 27703) OffRamp_manuallyExecute:test_manuallyExecute_InvalidTokenGasOverride_Revert() (gas: 55274) -OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 489576) -OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 314392) -OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 2227930) +OffRamp_manuallyExecute:test_manuallyExecute_LowGasLimit_Success() (gas: 489352) +OffRamp_manuallyExecute:test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() (gas: 314370) +OffRamp_manuallyExecute:test_manuallyExecute_ReentrancyFails_Success() (gas: 2227706) OffRamp_manuallyExecute:test_manuallyExecute_SourceChainSelectorMismatch_Revert() (gas: 165133) -OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 225972) -OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 226534) -OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 774706) -OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 344831) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 37632) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 104648) +OffRamp_manuallyExecute:test_manuallyExecute_Success() (gas: 225844) +OffRamp_manuallyExecute:test_manuallyExecute_WithGasOverride_Success() (gas: 226384) +OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success() (gas: 773426) +OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 344159) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 37654) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 104625) OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 83092) OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 36812) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 94648) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 37301) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 94670) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 37323) OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 86760) OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 162911) -OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 23881) +OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 23836) OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() (gas: 62844) OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() (gas: 80014) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 175034) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_Success() (gas: 174989) OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_WithGasOverride_Success() (gas: 176901) -OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 188145) +OffRamp_releaseOrMintTokens:test_releaseOrMintTokens_destDenominatedDecimals_Success() (gas: 188167) OffRamp_setDynamicConfig:test_FeeQuoterZeroAddress_Revert() (gas: 11509) OffRamp_setDynamicConfig:test_NonOwner_Revert() (gas: 14019) -OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 47591) -OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 25564) -OffRamp_trialExecute:test_RateLimitError_Success() (gas: 219989) -OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 228644) -OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 295794) -OffRamp_trialExecute:test_trialExecute_Success() (gas: 278096) +OffRamp_setDynamicConfig:test_SetDynamicConfigWithInterceptor_Success() (gas: 47579) +OffRamp_setDynamicConfig:test_SetDynamicConfig_Success() (gas: 25552) +OffRamp_trialExecute:test_RateLimitError_Success() (gas: 219928) +OffRamp_trialExecute:test_TokenHandlingErrorIsCaught_Success() (gas: 228561) +OffRamp_trialExecute:test_TokenPoolIsNotAContract_Success() (gas: 295602) +OffRamp_trialExecute:test_trialExecute_Success() (gas: 278032) OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 251573) OnRamp_applyAllowlistUpdates:test_applyAllowlistUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() (gas: 17227) OnRamp_applyAllowlistUpdates:test_applyAllowlistUpdates_Revert() (gas: 67101) OnRamp_applyAllowlistUpdates:test_applyAllowlistUpdates_Success() (gas: 325983) OnRamp_applyDestChainConfigUpdates:test_ApplyDestChainConfigUpdates_Success() (gas: 65892) OnRamp_applyDestChainConfigUpdates:test_ApplyDestChainConfigUpdates_WithInvalidChainSelector_Revert() (gas: 12902) -OnRamp_constructor:test_Constructor_EnableAllowList_ForwardFromRouter_Reverts() (gas: 2569385) +OnRamp_constructor:test_Constructor_EnableAllowList_ForwardFromRouter_Reverts() (gas: 2569362) OnRamp_constructor:test_Constructor_InvalidConfigChainSelectorEqZero_Revert() (gas: 95148) OnRamp_constructor:test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() (gas: 93090) OnRamp_constructor:test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() (gas: 98066) -OnRamp_constructor:test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() (gas: 93124) -OnRamp_constructor:test_Constructor_Success() (gas: 2647534) -OnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 115376) +OnRamp_constructor:test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() (gas: 93146) +OnRamp_constructor:test_Constructor_Success() (gas: 2647459) +OnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() (gas: 115398) OnRamp_forwardFromRouter:test_ForwardFromRouterExtraArgsV2_Success() (gas: 146244) -OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 145819) -OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 144046) -OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 146016) -OnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 145414) +OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessCustomExtraArgs() (gas: 145841) +OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessEmptyExtraArgs() (gas: 143957) +OnRamp_forwardFromRouter:test_ForwardFromRouterSuccessLegacyExtraArgs() (gas: 146038) +OnRamp_forwardFromRouter:test_ForwardFromRouter_Success() (gas: 145436) OnRamp_forwardFromRouter:test_ForwardFromRouter_Success_ConfigurableSourceRouter() (gas: 140697) OnRamp_forwardFromRouter:test_InvalidExtraArgsTag_Revert() (gas: 38504) -OnRamp_forwardFromRouter:test_MessageInterceptionError_Revert() (gas: 143100) +OnRamp_forwardFromRouter:test_MessageInterceptionError_Revert() (gas: 143122) OnRamp_forwardFromRouter:test_MesssageFeeTooHigh_Revert() (gas: 36611) -OnRamp_forwardFromRouter:test_MultiCannotSendZeroTokens_Revert() (gas: 36471) -OnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 18268) -OnRamp_forwardFromRouter:test_Paused_Revert() (gas: 38412) -OnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 23629) +OnRamp_forwardFromRouter:test_MultiCannotSendZeroTokens_Revert() (gas: 36493) +OnRamp_forwardFromRouter:test_OriginalSender_Revert() (gas: 18290) +OnRamp_forwardFromRouter:test_Paused_Revert() (gas: 38434) +OnRamp_forwardFromRouter:test_Permissions_Revert() (gas: 23651) OnRamp_forwardFromRouter:test_ShouldIncrementNonceOnlyOnOrdered_Success() (gas: 186649) OnRamp_forwardFromRouter:test_ShouldIncrementSeqNumAndNonce_Success() (gas: 213078) -OnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 147003) +OnRamp_forwardFromRouter:test_ShouldStoreLinkFees() (gas: 147025) OnRamp_forwardFromRouter:test_ShouldStoreNonLinkFees() (gas: 161214) -OnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3566334) +OnRamp_forwardFromRouter:test_SourceTokenDataTooLarge_Revert() (gas: 3576260) OnRamp_forwardFromRouter:test_UnAllowedOriginalSender_Revert() (gas: 24015) -OnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 75832) -OnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 38588) +OnRamp_forwardFromRouter:test_UnsupportedToken_Revert() (gas: 75854) +OnRamp_forwardFromRouter:test_forwardFromRouter_UnsupportedToken_Revert() (gas: 38610) OnRamp_forwardFromRouter:test_forwardFromRouter_WithInterception_Success() (gas: 280344) -OnRamp_getFee:test_EmptyMessage_Success() (gas: 98782) -OnRamp_getFee:test_EnforceOutOfOrder_Revert() (gas: 65498) -OnRamp_getFee:test_GetFeeOfZeroForTokenMessage_Success() (gas: 87208) -OnRamp_getFee:test_NotAFeeTokenButPricedToken_Revert() (gas: 35167) -OnRamp_getFee:test_SingleTokenMessage_Success() (gas: 113990) -OnRamp_getFee:test_Unhealthy_Revert() (gas: 17108) +OnRamp_getFee:test_EmptyMessage_Success() (gas: 98692) +OnRamp_getFee:test_EnforceOutOfOrder_Revert() (gas: 65453) +OnRamp_getFee:test_GetFeeOfZeroForTokenMessage_Success() (gas: 87185) +OnRamp_getFee:test_NotAFeeTokenButPricedToken_Revert() (gas: 35166) +OnRamp_getFee:test_SingleTokenMessage_Success() (gas: 113865) +OnRamp_getFee:test_Unhealthy_Revert() (gas: 17040) OnRamp_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10565) OnRamp_getTokenPool:test_GetTokenPool_Success() (gas: 35405) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigFeeAggregatorEqAddressZero_Revert() (gas: 11558) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigFeeAggregatorEqAddressZero_Revert() (gas: 11535) OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigFeeQuoterEqAddressZero_Revert() (gas: 13194) OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigInvalidConfig_Revert() (gas: 11499) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigOnlyOwner_Revert() (gas: 16648) -OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigReentrancyGuardEnteredEqTrue_Revert() (gas: 13220) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigOnlyOwner_Revert() (gas: 11938) +OnRamp_setDynamicConfig:test_setDynamicConfig_InvalidConfigReentrancyGuardEnteredEqTrue_Revert() (gas: 13264) OnRamp_setDynamicConfig:test_setDynamicConfig_Success() (gas: 56440) OnRamp_withdrawFeeTokens:test_WithdrawFeeTokens_Success() (gas: 125867) PingPong_ccipReceive:test_CcipReceive_Success() (gas: 172841) -PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20328) -PingPong_plumbing:test_Pausing_Success() (gas: 17760) +PingPong_plumbing:test_OutOfOrderExecution_Success() (gas: 20283) +PingPong_plumbing:test_Pausing_Success() (gas: 17738) PingPong_startPingPong:test_StartPingPong_With_OOO_Success() (gas: 151954) PingPong_startPingPong:test_StartPingPong_With_Sequenced_Ordered_Success() (gas: 177569) RMNHome__validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_DuplicateOffchainPublicKey_reverts() (gas: 18850) @@ -623,8 +598,8 @@ RMNRemote_verify_withConfigNotSet:test_verify_reverts() (gas: 13578) RMNRemote_verify_withConfigSet:test_verify_InvalidSignature_reverts() (gas: 96449) RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_duplicateSignature_reverts() (gas: 94267) RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_not_sorted_reverts() (gas: 101330) -RMNRemote_verify_withConfigSet:test_verify_ThresholdNotMet_reverts() (gas: 303866) -RMNRemote_verify_withConfigSet:test_verify_UnexpectedSigner_reverts() (gas: 427512) +RMNRemote_verify_withConfigSet:test_verify_ThresholdNotMet_reverts() (gas: 304634) +RMNRemote_verify_withConfigSet:test_verify_UnexpectedSigner_reverts() (gas: 428126) RMNRemote_verify_withConfigSet:test_verify_success() (gas: 86159) RateLimiter_constructor:test_Constructor_Success() (gas: 19806) RateLimiter_consume:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 16042) @@ -640,45 +615,47 @@ RateLimiter_consume:test_TokenRateLimitReached_Revert() (gas: 24930) RateLimiter_currentTokenBucketState:test_CurrentTokenBucketState_Success() (gas: 38947) RateLimiter_currentTokenBucketState:test_Refill_Success() (gas: 46852) RateLimiter_setTokenBucketConfig:test_SetRateLimiterConfig_Success() (gas: 38509) -RegistryModuleOwnerCustom_constructor:test_constructor_Revert() (gas: 36033) -RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Revert() (gas: 19763) -RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Success() (gas: 130104) -RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Revert() (gas: 19568) -RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Success() (gas: 129908) +RegistryModuleOwnerCustom_constructor:test_constructor_Revert() (gas: 36107) +RegistryModuleOwnerCustom_registerAccessControlDefaultAdmin:test_registerAccessControlDefaultAdmin_Revert() (gas: 20200) +RegistryModuleOwnerCustom_registerAccessControlDefaultAdmin:test_registerAccessControlDefaultAdmin_Success() (gas: 130631) +RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Revert() (gas: 19797) +RegistryModuleOwnerCustom_registerAdminViaGetCCIPAdmin:test_registerAdminViaGetCCIPAdmin_Success() (gas: 130126) +RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Revert() (gas: 19602) +RegistryModuleOwnerCustom_registerAdminViaOwner:test_registerAdminViaOwner_Success() (gas: 129930) Router_applyRampUpdates:test_OffRampMismatch_Revert() (gas: 89591) Router_applyRampUpdates:test_OffRampUpdatesWithRouting() (gas: 10749462) Router_applyRampUpdates:test_OnRampDisable() (gas: 56428) Router_applyRampUpdates:test_OnlyOwner_Revert() (gas: 12414) Router_ccipSend:test_CCIPSendLinkFeeNoTokenSuccess_gas() (gas: 131413) -Router_ccipSend:test_CCIPSendLinkFeeOneTokenSuccess_gas() (gas: 221307) +Router_ccipSend:test_CCIPSendLinkFeeOneTokenSuccess_gas() (gas: 221240) Router_ccipSend:test_FeeTokenAmountTooLow_Revert() (gas: 71841) Router_ccipSend:test_InvalidMsgValue() (gas: 32411) -Router_ccipSend:test_NativeFeeTokenInsufficientValue() (gas: 69502) -Router_ccipSend:test_NativeFeeTokenOverpay_Success() (gas: 193274) +Router_ccipSend:test_NativeFeeTokenInsufficientValue() (gas: 69524) +Router_ccipSend:test_NativeFeeTokenOverpay_Success() (gas: 193296) Router_ccipSend:test_NativeFeeTokenZeroValue() (gas: 61550) Router_ccipSend:test_NativeFeeToken_Success() (gas: 191900) -Router_ccipSend:test_NonLinkFeeToken_Success() (gas: 226510) +Router_ccipSend:test_NonLinkFeeToken_Success() (gas: 226532) Router_ccipSend:test_UnsupportedDestinationChain_Revert() (gas: 25056) -Router_ccipSend:test_WhenNotHealthy_Revert() (gas: 45034) +Router_ccipSend:test_WhenNotHealthy_Revert() (gas: 45056) Router_ccipSend:test_WrappedNativeFeeToken_Success() (gas: 194209) Router_ccipSend:test_ccipSend_nativeFeeNoTokenSuccess_gas() (gas: 140674) -Router_ccipSend:test_ccipSend_nativeFeeOneTokenSuccess_gas() (gas: 230481) -Router_constructor:test_Constructor_Success() (gas: 13155) +Router_ccipSend:test_ccipSend_nativeFeeOneTokenSuccess_gas() (gas: 230436) +Router_constructor:test_Constructor_Success() (gas: 13222) Router_getArmProxy:test_getArmProxy() (gas: 10573) -Router_getFee:test_GetFeeSupportedChain_Success() (gas: 51979) -Router_getFee:test_UnsupportedDestinationChain_Revert() (gas: 17430) +Router_getFee:test_GetFeeSupportedChain_Success() (gas: 51934) +Router_getFee:test_UnsupportedDestinationChain_Revert() (gas: 17385) Router_getSupportedTokens:test_GetSupportedTokens_Revert() (gas: 10565) -Router_recoverTokens:test_RecoverTokensInvalidRecipient_Revert() (gas: 11344) +Router_recoverTokens:test_RecoverTokensInvalidRecipient_Revert() (gas: 11410) Router_recoverTokens:test_RecoverTokensNoFunds_Revert() (gas: 20199) -Router_recoverTokens:test_RecoverTokensNonOwner_Revert() (gas: 11214) +Router_recoverTokens:test_RecoverTokensNonOwner_Revert() (gas: 11236) Router_recoverTokens:test_RecoverTokensValueReceiver_Revert() (gas: 349502) -Router_recoverTokens:test_RecoverTokens_Success() (gas: 52622) -Router_routeMessage:test_routeMessage_AutoExec_Success() (gas: 43367) -Router_routeMessage:test_routeMessage_ExecutionEvent_Success() (gas: 159649) -Router_routeMessage:test_routeMessage_ManualExec_Success() (gas: 35845) -Router_routeMessage:test_routeMessage_OnlyOffRamp_Revert() (gas: 25431) -Router_routeMessage:test_routeMessage_WhenNotHealthy_Revert() (gas: 44889) -Router_setWrappedNative:test_OnlyOwner_Revert() (gas: 10986) +Router_recoverTokens:test_RecoverTokens_Success() (gas: 52640) +Router_routeMessage:test_routeMessage_AutoExec_Success() (gas: 43213) +Router_routeMessage:test_routeMessage_ExecutionEvent_Success() (gas: 159418) +Router_routeMessage:test_routeMessage_ManualExec_Success() (gas: 35723) +Router_routeMessage:test_routeMessage_OnlyOffRamp_Revert() (gas: 25376) +Router_routeMessage:test_routeMessage_WhenNotHealthy_Revert() (gas: 44812) +Router_setWrappedNative:test_OnlyOwner_Revert() (gas: 11008) TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_OnlyPendingAdministrator_Revert() (gas: 51433) TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_Success() (gas: 44189) TokenAdminRegistry_addRegistryModule:test_addRegistryModule_OnlyOwner_Revert() (gas: 12662) @@ -702,13 +679,13 @@ TokenAdminRegistry_setPool:test_setPool_ZeroAddressRemovesPool_Success() (gas: 3 TokenAdminRegistry_transferAdminRole:test_transferAdminRole_OnlyAdministrator_Revert() (gas: 18202) TokenAdminRegistry_transferAdminRole:test_transferAdminRole_Success() (gas: 49592) TokenPoolFactoryTests:test_TokenPoolFactory_Constructor_Revert() (gas: 1039441) -TokenPoolFactoryTests:test_createTokenPoolLockRelease_ExistingToken_predict_Success() (gas: 11497148) -TokenPoolFactoryTests:test_createTokenPool_BurnFromMintTokenPool_Success() (gas: 5833878) -TokenPoolFactoryTests:test_createTokenPool_ExistingRemoteToken_AndPredictPool_Success() (gas: 12127839) -TokenPoolFactoryTests:test_createTokenPool_WithNoExistingRemoteContracts_predict_Success() (gas: 12464532) -TokenPoolFactoryTests:test_createTokenPool_WithNoExistingTokenOnRemoteChain_Success() (gas: 5687016) -TokenPoolFactoryTests:test_createTokenPool_WithRemoteTokenAndRemotePool_Success() (gas: 5830403) -TokenPoolWithAllowList_applyAllowListUpdates:test_AllowListNotEnabled_Revert() (gas: 1934078) +TokenPoolFactoryTests:test_createTokenPoolLockRelease_ExistingToken_predict_Success() (gas: 11591893) +TokenPoolFactoryTests:test_createTokenPool_BurnFromMintTokenPool_Success() (gas: 5848501) +TokenPoolFactoryTests:test_createTokenPool_ExistingRemoteToken_AndPredictPool_Success() (gas: 12227697) +TokenPoolFactoryTests:test_createTokenPool_WithNoExistingRemoteContracts_predict_Success() (gas: 12564414) +TokenPoolFactoryTests:test_createTokenPool_WithNoExistingTokenOnRemoteChain_Success() (gas: 5701802) +TokenPoolFactoryTests:test_createTokenPool_WithRemoteTokenAndRemotePool_Success() (gas: 5845024) +TokenPoolWithAllowList_applyAllowListUpdates:test_AllowListNotEnabled_Revert() (gas: 1944108) TokenPoolWithAllowList_applyAllowListUpdates:test_OnlyOwner_Revert() (gas: 12119) TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowListSkipsZero_Success() (gas: 23567) TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowList_Success() (gas: 178398) @@ -721,7 +698,7 @@ TokenPool_applyChainUpdates:test_applyChainUpdates_NonExistentChain_Revert() (ga TokenPool_applyChainUpdates:test_applyChainUpdates_OnlyCallableByOwner_Revert() (gas: 11425) TokenPool_applyChainUpdates:test_applyChainUpdates_Success() (gas: 480305) TokenPool_applyChainUpdates:test_applyChainUpdates_ZeroAddressNotAllowed_Revert() (gas: 157716) -TokenPool_constructor:test_ZeroAddressNotAllowed_Revert() (gas: 70430) +TokenPool_constructor:test_ZeroAddressNotAllowed_Revert() (gas: 70445) TokenPool_constructor:test_immutableFields_Success() (gas: 20718) TokenPool_getRemotePool:test_getRemotePool_Success() (gas: 274610) TokenPool_onlyOffRamp:test_CallerIsNotARampOnRouter_Revert() (gas: 277555) @@ -732,9 +709,42 @@ TokenPool_onlyOnRamp:test_ChainNotAllowed_Revert() (gas: 254443) TokenPool_onlyOnRamp:test_onlyOnRamp_Success() (gas: 305359) TokenPool_setChainRateLimiterConfig:test_NonExistentChain_Revert() (gas: 17225) TokenPool_setChainRateLimiterConfig:test_OnlyOwnerOrRateLimitAdmin_Revert() (gas: 15330) +TokenPool_setRateLimitAdmin:test_SetRateLimitAdmin_Revert() (gas: 11024) +TokenPool_setRateLimitAdmin:test_SetRateLimitAdmin_Success() (gas: 37584) TokenPool_setRemotePool:test_setRemotePool_NonExistentChain_Reverts() (gas: 15796) TokenPool_setRemotePool:test_setRemotePool_OnlyOwner_Reverts() (gas: 13285) TokenPool_setRemotePool:test_setRemotePool_Success() (gas: 282581) +USDCBridgeMigrator_BurnLockedUSDC:test_PrimaryMechanism_Success() (gas: 135930) +USDCBridgeMigrator_BurnLockedUSDC:test_WhileMigrationPause_Revert() (gas: 109773) +USDCBridgeMigrator_BurnLockedUSDC:test_invalidPermissions_Revert() (gas: 39343) +USDCBridgeMigrator_BurnLockedUSDC:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 309779) +USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism_Success() (gas: 147037) +USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism_thenswitchToPrimary_Success() (gas: 209263) +USDCBridgeMigrator_cancelMigrationProposal:test_cancelExistingCCTPMigrationProposal_Success() (gas: 56190) +USDCBridgeMigrator_cancelMigrationProposal:test_cannotCancelANonExistentMigrationProposal_Revert() (gas: 12691) +USDCBridgeMigrator_excludeTokensFromBurn:test_excludeTokensWhenNoMigrationProposalPending_Revert() (gas: 13579) +USDCBridgeMigrator_proposeMigration:test_ChainNotUsingLockRelease_Revert() (gas: 15765) +USDCBridgeMigrator_provideLiquidity:test_PrimaryMechanism_Success() (gas: 135912) +USDCBridgeMigrator_provideLiquidity:test_WhileMigrationPause_Revert() (gas: 109795) +USDCBridgeMigrator_provideLiquidity:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 13378) +USDCBridgeMigrator_provideLiquidity:test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() (gas: 67436) +USDCBridgeMigrator_provideLiquidity:test_cannotProvideLiquidity_AfterMigration_Revert() (gas: 313619) +USDCBridgeMigrator_provideLiquidity:test_invalidPermissions_Revert() (gas: 39343) +USDCBridgeMigrator_provideLiquidity:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 309779) +USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism_Success() (gas: 147082) +USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism_thenswitchToPrimary_Success() (gas: 209299) +USDCBridgeMigrator_releaseOrMint:test_OnLockReleaseMechanism_Success() (gas: 216942) +USDCBridgeMigrator_releaseOrMint:test_WhileMigrationPause_Revert() (gas: 113505) +USDCBridgeMigrator_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 269034) +USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_destChain_Success() (gas: 156071) +USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 516094) +USDCBridgeMigrator_updateChainSelectorMechanism:test_PrimaryMechanism_Success() (gas: 135930) +USDCBridgeMigrator_updateChainSelectorMechanism:test_WhileMigrationPause_Revert() (gas: 109773) +USDCBridgeMigrator_updateChainSelectorMechanism:test_cannotRevertChainMechanism_afterMigration_Revert() (gas: 313216) +USDCBridgeMigrator_updateChainSelectorMechanism:test_invalidPermissions_Revert() (gas: 39321) +USDCBridgeMigrator_updateChainSelectorMechanism:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 309779) +USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism_Success() (gas: 147037) +USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism_thenswitchToPrimary_Success() (gas: 209263) USDCTokenPool__validateMessage:test_ValidateInvalidMessage_Revert() (gas: 25854) USDCTokenPool_lockOrBurn:test_CallerIsNotARampOnRouter_Revert() (gas: 35504) USDCTokenPool_lockOrBurn:test_LockOrBurnWithAllowList_Revert() (gas: 30235) diff --git a/contracts/gas-snapshots/liquiditymanager.gas-snapshot b/contracts/gas-snapshots/liquiditymanager.gas-snapshot index 94742aea084..435e79b002e 100644 --- a/contracts/gas-snapshots/liquiditymanager.gas-snapshot +++ b/contracts/gas-snapshots/liquiditymanager.gas-snapshot @@ -3,9 +3,9 @@ LiquidityManager_addLiquidity:test_addLiquiditySuccess() (gas: 279198) LiquidityManager_rebalanceLiquidity:test_InsufficientLiquidityReverts() (gas: 206764) LiquidityManager_rebalanceLiquidity:test_InvalidRemoteChainReverts() (gas: 192374) LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess() (gas: 9141798) -LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess_AlreadyFinalized() (gas: 8932108) -LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_MultiStageFinalization() (gas: 8927248) -LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_NativeRewrap() (gas: 8854986) +LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess_AlreadyFinalized() (gas: 8942122) +LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_MultiStageFinalization() (gas: 8937262) +LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_NativeRewrap() (gas: 8865000) LiquidityManager_rebalanceLiquidity:test_rebalanceLiquiditySuccess() (gas: 382946) LiquidityManager_receive:test_receive_success() (gas: 21182) LiquidityManager_removeLiquidity:test_InsufficientLiquidityReverts() (gas: 184959) @@ -19,7 +19,7 @@ LiquidityManager_setFinanceRole:test_OnlyOwnerReverts() (gas: 10987) LiquidityManager_setFinanceRole:test_setFinanceRoleSuccess() (gas: 21836) LiquidityManager_setLocalLiquidityContainer:test_OnlyOwnerReverts() (gas: 11030) LiquidityManager_setLocalLiquidityContainer:test_ReverstWhen_CalledWithTheZeroAddress() (gas: 10621) -LiquidityManager_setLocalLiquidityContainer:test_setLocalLiquidityContainerSuccess() (gas: 3469880) +LiquidityManager_setLocalLiquidityContainer:test_setLocalLiquidityContainerSuccess() (gas: 3479905) LiquidityManager_setMinimumLiquidity:test_OnlyOwnerReverts() (gas: 10925) LiquidityManager_setMinimumLiquidity:test_setMinimumLiquiditySuccess() (gas: 36389) LiquidityManager_withdrawERC20:test_withdrawERC20Reverts() (gas: 180396) diff --git a/contracts/package.json b/contracts/package.json index 2c23043f3f2..731b695f636 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/contracts", - "version": "1.2.0", + "version": "1.3.0", "description": "Chainlink smart contracts", "author": "Chainlink devs", "license": "MIT", @@ -18,7 +18,8 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "pnpm publish --tag latest", - "solhint": "solhint --max-warnings 0 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 0 \"./src/v0.8/**/*.sol\"", + "solhint-test": "solhint --config \".solhint-test.json\" --ignore-path \".solhintignore-test\" --max-warnings 0 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", diff --git a/contracts/src/v0.8/ccip/pools/TokenPool.sol b/contracts/src/v0.8/ccip/pools/TokenPool.sol index 5e267276ab0..ac54d93af25 100644 --- a/contracts/src/v0.8/ccip/pools/TokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/TokenPool.sol @@ -53,6 +53,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { event AllowListAdd(address sender); event AllowListRemove(address sender); event RouterUpdated(address oldRouter, address newRouter); + event RateLimitAdminSet(address rateLimitAdmin); struct ChainUpdate { uint64 remoteChainSelector; // ──╮ Remote chain selector @@ -326,6 +327,7 @@ abstract contract TokenPool is IPoolV1, Ownable2StepMsgSender { address rateLimitAdmin ) external onlyOwner { s_rateLimitAdmin = rateLimitAdmin; + emit RateLimitAdminSet(rateLimitAdmin); } /// @notice Gets the rate limiter admin address. diff --git a/contracts/src/v0.8/ccip/test/BaseTest.t.sol b/contracts/src/v0.8/ccip/test/BaseTest.t.sol index ea75b4eda99..2770f0fb4d6 100644 --- a/contracts/src/v0.8/ccip/test/BaseTest.t.sol +++ b/contracts/src/v0.8/ccip/test/BaseTest.t.sol @@ -14,15 +14,8 @@ contract BaseTest is Test { // Addresses address internal constant OWNER = 0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e; address internal constant STRANGER = address(999999); - address internal constant DUMMY_CONTRACT_ADDRESS = 0x1111111111111111111111111111111111111112; - address internal constant ON_RAMP_ADDRESS = 0x11118e64e1FB0c487f25dD6D3601FF6aF8d32E4e; - address internal constant ZERO_ADDRESS = address(0); - address internal constant FEE_AGGREGATOR = 0xa33CDB32eAEce34F6affEfF4899cef45744EDea3; address internal constant USER_1 = address(1); - address internal constant USER_2 = address(2); - address internal constant USER_3 = address(3); - address internal constant USER_4 = address(4); // Message info uint64 internal constant SOURCE_CHAIN_SELECTOR = 1; @@ -34,7 +27,6 @@ contract BaseTest is Test { uint32 internal constant TWELVE_HOURS = 60 * 60 * 12; // Onramp - uint96 internal constant MAX_NOP_FEES_JUELS = 1e27; uint96 internal constant MAX_MSG_FEES_JUELS = 1_000e18; uint32 internal constant DEST_GAS_OVERHEAD = 300_000; uint16 internal constant DEST_GAS_PER_PAYLOAD_BYTE = 16; @@ -45,36 +37,17 @@ contract BaseTest is Test { bool private s_baseTestInitialized; - // Use 16 gas per data availability byte in our tests. - // This is an overestimation in OP stack, it ignores 4 gas per 0 byte rule. - // Arbitrum on the other hand, does always use 16 gas per data availability byte. - // This value may be substantially decreased after EIP 4844. - uint16 internal constant DEST_GAS_PER_DATA_AVAILABILITY_BYTE = 16; - - // Total L1 data availability overhead estimate is 33_596 gas. - // This value includes complete CommitStore and OffRamp call data. - uint32 internal constant DEST_DATA_AVAILABILITY_OVERHEAD_GAS = 188 // Fixed data availability overhead in OP stack. - + (32 * 31 + 4) * DEST_GAS_PER_DATA_AVAILABILITY_BYTE // CommitStore single-root transmission takes up about 31 slots, plus selector. - + (32 * 34 + 4) * DEST_GAS_PER_DATA_AVAILABILITY_BYTE; // OffRamp transmission excluding EVM2EVMMessage takes up about 34 slots, plus selector. - - // Multiples of bps, or 0.0001, use 6840 to be same as OP mainnet compression factor of 0.684. - uint16 internal constant DEST_GAS_DATA_AVAILABILITY_MULTIPLIER_BPS = 6840; - // OffRamp uint32 internal constant MAX_DATA_SIZE = 30_000; uint16 internal constant MAX_TOKENS_LENGTH = 5; uint16 internal constant GAS_FOR_CALL_EXACT_CHECK = 5000; - uint32 internal constant PERMISSION_LESS_EXECUTION_THRESHOLD_SECONDS = 500; uint32 internal constant MAX_GAS_LIMIT = 4_000_000; - // Rate limiter - address internal constant ADMIN = 0x11118e64e1FB0c487f25dD6D3601FF6aF8d32E4e; - MockRMN internal s_mockRMN; IRMNRemote internal s_mockRMNRemote; // nonce for pseudo-random number generation, not to be exposed to test suites - uint256 private randNonce; + uint256 private s_randNonce; function setUp() public virtual { // BaseTest.setUp is often called multiple times from tests' setUp due to inheritance. @@ -136,7 +109,7 @@ contract BaseTest is Test { /// @dev returns a pseudo-random bytes32 function _randomBytes32() internal returns (bytes32) { - return keccak256(abi.encodePacked(++randNonce)); + return keccak256(abi.encodePacked(++s_randNonce)); } /// @dev returns a pseudo-random number diff --git a/contracts/src/v0.8/ccip/test/NonceManager.t.sol b/contracts/src/v0.8/ccip/test/NonceManager.t.sol index 30b11df32e6..f560b5be593 100644 --- a/contracts/src/v0.8/ccip/test/NonceManager.t.sol +++ b/contracts/src/v0.8/ccip/test/NonceManager.t.sol @@ -4,18 +4,15 @@ pragma solidity 0.8.24; import {IEVM2AnyOnRamp} from "../interfaces/IEVM2AnyOnRamp.sol"; import {NonceManager} from "../NonceManager.sol"; -import {Router} from "../Router.sol"; import {Client} from "../libraries/Client.sol"; import {Internal} from "../libraries/Internal.sol"; -import {Pool} from "../libraries/Pool.sol"; -import {RateLimiter} from "../libraries/RateLimiter.sol"; import {OffRamp} from "../offRamp/OffRamp.sol"; import {OnRamp} from "../onRamp/OnRamp.sol"; import {BaseTest} from "./BaseTest.t.sol"; import {EVM2EVMOffRampHelper} from "./helpers/EVM2EVMOffRampHelper.sol"; import {OnRampHelper} from "./helpers/OnRampHelper.sol"; -import {OffRampSetup} from "./offRamp/OffRampSetup.t.sol"; -import {OnRampSetup} from "./onRamp/OnRampSetup.t.sol"; +import {OffRampSetup} from "./offRamp/offRamp/OffRampSetup.t.sol"; +import {OnRampSetup} from "./onRamp/onRamp/OnRampSetup.t.sol"; import {Test} from "forge-std/Test.sol"; @@ -380,7 +377,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber, messages[0].header.messageId, @@ -408,7 +405,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messagesChain3), new OffRamp.GasLimitOverride[](0) ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR_3, messagesChain3[0].header.sequenceNumber, messagesChain3[0].header.messageId, @@ -456,7 +453,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new OffRamp.GasLimitOverride[](0) ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp[0].header.sequenceNumber, messagesMultiRamp[0].header.messageId, @@ -477,7 +474,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new OffRamp.GasLimitOverride[](0) ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp[0].header.sequenceNumber, messagesMultiRamp[0].header.messageId, @@ -510,7 +507,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { s_offRamp.executeSingleReport( _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp), new OffRamp.GasLimitOverride[](0) ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR_1, messagesMultiRamp[0].header.sequenceNumber, messagesMultiRamp[0].header.messageId, @@ -557,7 +554,7 @@ contract NonceManager_OffRampUpgrade is OffRampSetup { _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber, messages[0].header.messageId, diff --git a/contracts/src/v0.8/ccip/test/TokenSetup.t.sol b/contracts/src/v0.8/ccip/test/TokenSetup.t.sol index 203145881e3..42d10190f1e 100644 --- a/contracts/src/v0.8/ccip/test/TokenSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/TokenSetup.t.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.24; import {BurnMintERC677} from "../../shared/token/ERC677/BurnMintERC677.sol"; -import {Client} from "../libraries/Client.sol"; import {BurnMintTokenPool} from "../pools/BurnMintTokenPool.sol"; import {LockReleaseTokenPool} from "../pools/LockReleaseTokenPool.sol"; import {TokenPool} from "../pools/TokenPool.sol"; @@ -136,18 +135,6 @@ contract TokenSetup is RouterSetup { } } - function _getCastedSourceEVMTokenAmountsWithZeroAmounts() - internal - view - returns (Client.EVMTokenAmount[] memory tokenAmounts) - { - tokenAmounts = new Client.EVMTokenAmount[](s_sourceTokens.length); - for (uint256 i = 0; i < tokenAmounts.length; ++i) { - tokenAmounts[i].token = s_sourceTokens[i]; - } - return tokenAmounts; - } - function _setPool( TokenAdminRegistry tokenAdminRegistry, address token, diff --git a/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol b/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol index c68907bb9f9..b4829668ce3 100644 --- a/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol +++ b/contracts/src/v0.8/ccip/test/applications/DefensiveExample.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import {DefensiveExample} from "../../applications/DefensiveExample.sol"; import {Client} from "../../libraries/Client.sol"; -import {OnRampSetup} from "../onRamp/OnRampSetup.t.sol"; +import {OnRampSetup} from "../onRamp/onRamp/OnRampSetup.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; @@ -13,13 +13,13 @@ contract DefensiveExampleTest is OnRampSetup { event MessageRecovered(bytes32 indexed messageId); DefensiveExample internal s_receiver; - uint64 internal sourceChainSelector = 7331; + uint64 internal s_sourceChainSelector = 7331; function setUp() public virtual override { super.setUp(); s_receiver = new DefensiveExample(s_destRouter, IERC20(s_destFeeToken)); - s_receiver.enableChain(sourceChainSelector, abi.encode("")); + s_receiver.enableChain(s_sourceChainSelector, abi.encode("")); } function test_Recovery() public { @@ -44,7 +44,7 @@ contract DefensiveExampleTest is OnRampSetup { s_receiver.ccipReceive( Client.Any2EVMMessage({ messageId: messageId, - sourceChainSelector: sourceChainSelector, + sourceChainSelector: s_sourceChainSelector, sender: abi.encode(address(0)), // wrong sender, will revert internally data: "", destTokenAmounts: destTokenAmounts @@ -87,7 +87,7 @@ contract DefensiveExampleTest is OnRampSetup { s_receiver.ccipReceive( Client.Any2EVMMessage({ messageId: messageId, - sourceChainSelector: sourceChainSelector, + sourceChainSelector: s_sourceChainSelector, sender: abi.encode(address(s_receiver)), // correct sender data: "", destTokenAmounts: destTokenAmounts diff --git a/contracts/src/v0.8/ccip/test/applications/EtherSenderReceiver.t.sol b/contracts/src/v0.8/ccip/test/applications/EtherSenderReceiver.t.sol index 1791d784eed..6da86a06c7e 100644 --- a/contracts/src/v0.8/ccip/test/applications/EtherSenderReceiver.t.sol +++ b/contracts/src/v0.8/ccip/test/applications/EtherSenderReceiver.t.sol @@ -21,6 +21,7 @@ contract EtherSenderReceiverTest is Test { address internal constant OWNER = 0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e; address internal constant ROUTER = 0x0F3779ee3a832D10158073ae2F5e61ac7FBBF880; address internal constant XCHAIN_RECEIVER = 0xBd91b2073218AF872BF73b65e2e5950ea356d147; + uint256 internal constant AMOUNT = 100; function setUp() public { vm.startPrank(OWNER); @@ -50,14 +51,12 @@ contract EtherSenderReceiverTest_constructor is EtherSenderReceiverTest { } contract EtherSenderReceiverTest_validateFeeToken is EtherSenderReceiverTest { - uint256 internal constant amount = 100; - error InsufficientMsgValue(uint256 gotAmount, uint256 msgValue); error TokenAmountNotEqualToMsgValue(uint256 gotAmount, uint256 msgValue); function test_validateFeeToken_valid_native() public { Client.EVMTokenAmount[] memory tokenAmount = new Client.EVMTokenAmount[](1); - tokenAmount[0] = Client.EVMTokenAmount({token: address(s_weth), amount: amount}); + tokenAmount[0] = Client.EVMTokenAmount({token: address(s_weth), amount: AMOUNT}); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), data: "", @@ -66,12 +65,12 @@ contract EtherSenderReceiverTest_validateFeeToken is EtherSenderReceiverTest { extraArgs: "" }); - s_etherSenderReceiver.validateFeeToken{value: amount + 1}(message); + s_etherSenderReceiver.validateFeeToken{value: AMOUNT + 1}(message); } function test_validateFeeToken_valid_feeToken() public { Client.EVMTokenAmount[] memory tokenAmount = new Client.EVMTokenAmount[](1); - tokenAmount[0] = Client.EVMTokenAmount({token: address(s_weth), amount: amount}); + tokenAmount[0] = Client.EVMTokenAmount({token: address(s_weth), amount: AMOUNT}); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), data: "", @@ -80,12 +79,12 @@ contract EtherSenderReceiverTest_validateFeeToken is EtherSenderReceiverTest { extraArgs: "" }); - s_etherSenderReceiver.validateFeeToken{value: amount}(message); + s_etherSenderReceiver.validateFeeToken{value: AMOUNT}(message); } function test_validateFeeToken_reverts_feeToken_tokenAmountNotEqualToMsgValue() public { Client.EVMTokenAmount[] memory tokenAmount = new Client.EVMTokenAmount[](1); - tokenAmount[0] = Client.EVMTokenAmount({token: address(s_weth), amount: amount}); + tokenAmount[0] = Client.EVMTokenAmount({token: address(s_weth), amount: AMOUNT}); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), data: "", @@ -94,8 +93,8 @@ contract EtherSenderReceiverTest_validateFeeToken is EtherSenderReceiverTest { extraArgs: "" }); - vm.expectRevert(abi.encodeWithSelector(TokenAmountNotEqualToMsgValue.selector, amount, amount + 1)); - s_etherSenderReceiver.validateFeeToken{value: amount + 1}(message); + vm.expectRevert(abi.encodeWithSelector(TokenAmountNotEqualToMsgValue.selector, AMOUNT, AMOUNT + 1)); + s_etherSenderReceiver.validateFeeToken{value: AMOUNT + 1}(message); } } @@ -105,15 +104,13 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { error InvalidWethAddress(address want, address got); error GasLimitTooLow(uint256 minLimit, uint256 gotLimit); - uint256 internal constant amount = 100; - function test_Fuzz_validatedMessage_msgSenderOverwrite( bytes memory data ) public view { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -127,7 +124,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { assertEq(validatedMessage.receiver, abi.encode(XCHAIN_RECEIVER), "receiver must be XCHAIN_RECEIVER"); assertEq(validatedMessage.data, abi.encode(OWNER), "data must be msg.sender"); assertEq(validatedMessage.tokenAmounts[0].token, address(s_weth), "token must be weth"); - assertEq(validatedMessage.tokenAmounts[0].amount, amount, "amount must be correct"); + assertEq(validatedMessage.tokenAmounts[0].amount, AMOUNT, "amount must be correct"); assertEq(validatedMessage.feeToken, address(0), "feeToken must be 0"); assertEq(validatedMessage.extraArgs, bytes(""), "extraArgs must be empty"); } @@ -136,7 +133,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { address token ) public view { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); - tokenAmounts[0] = Client.EVMTokenAmount({token: token, amount: amount}); + tokenAmounts[0] = Client.EVMTokenAmount({token: token, amount: AMOUNT}); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), data: "", @@ -149,7 +146,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { assertEq(validatedMessage.receiver, abi.encode(XCHAIN_RECEIVER), "receiver must be XCHAIN_RECEIVER"); assertEq(validatedMessage.data, abi.encode(OWNER), "data must be msg.sender"); assertEq(validatedMessage.tokenAmounts[0].token, address(s_weth), "token must be weth"); - assertEq(validatedMessage.tokenAmounts[0].amount, amount, "amount must be correct"); + assertEq(validatedMessage.tokenAmounts[0].amount, AMOUNT, "amount must be correct"); assertEq(validatedMessage.feeToken, address(0), "feeToken must be 0"); assertEq(validatedMessage.extraArgs, bytes(""), "extraArgs must be empty"); } @@ -158,7 +155,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -172,7 +169,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { assertEq(validatedMessage.receiver, abi.encode(XCHAIN_RECEIVER), "receiver must be XCHAIN_RECEIVER"); assertEq(validatedMessage.data, abi.encode(OWNER), "data must be msg.sender"); assertEq(validatedMessage.tokenAmounts[0].token, address(s_weth), "token must be weth"); - assertEq(validatedMessage.tokenAmounts[0].amount, amount, "amount must be correct"); + assertEq(validatedMessage.tokenAmounts[0].amount, AMOUNT, "amount must be correct"); assertEq(validatedMessage.feeToken, address(0), "feeToken must be 0"); assertEq(validatedMessage.extraArgs, bytes(""), "extraArgs must be empty"); } @@ -181,7 +178,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -195,7 +192,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { assertEq(validatedMessage.receiver, abi.encode(XCHAIN_RECEIVER), "receiver must be XCHAIN_RECEIVER"); assertEq(validatedMessage.data, abi.encode(OWNER), "data must be msg.sender"); assertEq(validatedMessage.tokenAmounts[0].token, address(s_weth), "token must be weth"); - assertEq(validatedMessage.tokenAmounts[0].amount, amount, "amount must be correct"); + assertEq(validatedMessage.tokenAmounts[0].amount, AMOUNT, "amount must be correct"); assertEq(validatedMessage.feeToken, address(0), "feeToken must be 0"); assertEq(validatedMessage.extraArgs, bytes(""), "extraArgs must be empty"); } @@ -204,7 +201,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(42), // incorrect token. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -218,7 +215,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { assertEq(validatedMessage.receiver, abi.encode(XCHAIN_RECEIVER), "receiver must be XCHAIN_RECEIVER"); assertEq(validatedMessage.data, abi.encode(OWNER), "data must be msg.sender"); assertEq(validatedMessage.tokenAmounts[0].token, address(s_weth), "token must be weth"); - assertEq(validatedMessage.tokenAmounts[0].amount, amount, "amount must be correct"); + assertEq(validatedMessage.tokenAmounts[0].amount, AMOUNT, "amount must be correct"); assertEq(validatedMessage.feeToken, address(0), "feeToken must be 0"); assertEq(validatedMessage.extraArgs, bytes(""), "extraArgs must be empty"); } @@ -227,7 +224,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -241,7 +238,7 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { assertEq(validatedMessage.receiver, abi.encode(XCHAIN_RECEIVER), "receiver must be XCHAIN_RECEIVER"); assertEq(validatedMessage.data, abi.encode(OWNER), "data must be msg.sender"); assertEq(validatedMessage.tokenAmounts[0].token, address(s_weth), "token must be weth"); - assertEq(validatedMessage.tokenAmounts[0].amount, amount, "amount must be correct"); + assertEq(validatedMessage.tokenAmounts[0].amount, AMOUNT, "amount must be correct"); assertEq(validatedMessage.feeToken, address(0), "feeToken must be 0"); assertEq( validatedMessage.extraArgs, @@ -252,8 +249,8 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { function test_validatedMessage_invalidTokenAmounts() public { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](2); - tokenAmounts[0] = Client.EVMTokenAmount({token: address(0), amount: amount}); - tokenAmounts[1] = Client.EVMTokenAmount({token: address(0), amount: amount}); + tokenAmounts[0] = Client.EVMTokenAmount({token: address(0), amount: AMOUNT}); + tokenAmounts[1] = Client.EVMTokenAmount({token: address(0), amount: AMOUNT}); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), data: "", @@ -268,13 +265,12 @@ contract EtherSenderReceiverTest_validatedMessage is EtherSenderReceiverTest { } contract EtherSenderReceiverTest_getFee is EtherSenderReceiverTest { - uint64 internal constant destinationChainSelector = 424242; - uint256 internal constant feeWei = 121212; - uint256 internal constant amount = 100; + uint64 internal constant DESTINATION_CHAIN_SELECTOR = 424242; + uint256 internal constant FEE_WEI = 121212; function test_getFee() public { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); - tokenAmounts[0] = Client.EVMTokenAmount({token: address(0), amount: amount}); + tokenAmounts[0] = Client.EVMTokenAmount({token: address(0), amount: AMOUNT}); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), data: "", @@ -287,18 +283,17 @@ contract EtherSenderReceiverTest_getFee is EtherSenderReceiverTest { vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeWei) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_WEI) ); - uint256 fee = s_etherSenderReceiver.getFee(destinationChainSelector, message); - assertEq(fee, feeWei, "fee must be feeWei"); + uint256 fee = s_etherSenderReceiver.getFee(DESTINATION_CHAIN_SELECTOR, message); + assertEq(fee, FEE_WEI, "fee must be feeWei"); } } contract EtherSenderReceiverTest_ccipReceive is EtherSenderReceiverTest { - uint256 internal constant amount = 100; - uint64 internal constant sourceChainSelector = 424242; + uint64 internal constant SOURCE_CHAIN_SELECTOR = 424242; address internal constant XCHAIN_SENDER = 0x9951529C13B01E542f7eE3b6D6665D292e9BA2E0; error InvalidTokenAmounts(uint256 gotAmounts); @@ -316,7 +311,7 @@ contract EtherSenderReceiverTest_ccipReceive is EtherSenderReceiverTest { destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_weth), amount: tokenAmount}); Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ messageId: keccak256(abi.encode("ccip send")), - sourceChainSelector: sourceChainSelector, + sourceChainSelector: SOURCE_CHAIN_SELECTOR, sender: abi.encode(XCHAIN_SENDER), data: abi.encode(OWNER), destTokenAmounts: destTokenAmounts @@ -333,7 +328,7 @@ contract EtherSenderReceiverTest_ccipReceive is EtherSenderReceiverTest { function test_ccipReceive_happyPath() public { Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1); - destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_weth), amount: amount}); + destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_weth), amount: AMOUNT}); Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ messageId: keccak256(abi.encode("ccip send")), sourceChainSelector: 424242, @@ -343,17 +338,17 @@ contract EtherSenderReceiverTest_ccipReceive is EtherSenderReceiverTest { }); // simulate a cross-chain token transfer, just transfer the weth to s_etherSenderReceiver. - s_weth.transfer(address(s_etherSenderReceiver), amount); + s_weth.transfer(address(s_etherSenderReceiver), AMOUNT); uint256 balanceBefore = OWNER.balance; s_etherSenderReceiver.publicCcipReceive(message); uint256 balanceAfter = OWNER.balance; - assertEq(balanceAfter, balanceBefore + amount, "balance must be correct"); + assertEq(balanceAfter, balanceBefore + AMOUNT, "balance must be correct"); } function test_ccipReceive_fallbackToWethTransfer() public { Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1); - destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_weth), amount: amount}); + destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_weth), amount: AMOUNT}); Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ messageId: keccak256(abi.encode("ccip send")), sourceChainSelector: 424242, @@ -363,20 +358,20 @@ contract EtherSenderReceiverTest_ccipReceive is EtherSenderReceiverTest { }); // simulate a cross-chain token transfer, just transfer the weth to s_etherSenderReceiver. - s_weth.transfer(address(s_etherSenderReceiver), amount); + s_weth.transfer(address(s_etherSenderReceiver), AMOUNT); uint256 balanceBefore = address(s_linkToken).balance; s_etherSenderReceiver.publicCcipReceive(message); uint256 balanceAfter = address(s_linkToken).balance; assertEq(balanceAfter, balanceBefore, "balance must be unchanged"); uint256 wethBalance = s_weth.balanceOf(address(s_linkToken)); - assertEq(wethBalance, amount, "weth balance must be correct"); + assertEq(wethBalance, AMOUNT, "weth balance must be correct"); } function test_ccipReceive_wrongTokenAmount() public { Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](2); - destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_weth), amount: amount}); - destTokenAmounts[1] = Client.EVMTokenAmount({token: address(s_weth), amount: amount}); + destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_weth), amount: AMOUNT}); + destTokenAmounts[1] = Client.EVMTokenAmount({token: address(s_weth), amount: AMOUNT}); Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ messageId: keccak256(abi.encode("ccip send")), sourceChainSelector: 424242, @@ -391,7 +386,7 @@ contract EtherSenderReceiverTest_ccipReceive is EtherSenderReceiverTest { function test_ccipReceive_wrongToken() public { Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1); - destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_someOtherWeth), amount: amount}); + destTokenAmounts[0] = Client.EVMTokenAmount({token: address(s_someOtherWeth), amount: AMOUNT}); Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ messageId: keccak256(abi.encode("ccip send")), sourceChainSelector: 424242, @@ -408,19 +403,18 @@ contract EtherSenderReceiverTest_ccipReceive is EtherSenderReceiverTest { contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { error InsufficientFee(uint256 gotFee, uint256 fee); - uint256 internal constant amount = 100; - uint64 internal constant destinationChainSelector = 424242; - uint256 internal constant feeWei = 121212; - uint256 internal constant feeJuels = 232323; + uint64 internal constant DESTINATION_CHAIN_SELECTOR = 424242; + uint256 internal constant FEE_WEI = 121212; + uint256 internal constant FEE_JUELS = 232323; function test_Fuzz_ccipSend(uint256 feeFromRouter, uint256 feeSupplied) public { // cap the fuzzer because OWNER only has a million ether. - vm.assume(feeSupplied < 1_000_000 ether - amount); + vm.assume(feeSupplied < 1_000_000 ether - AMOUNT); Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -434,36 +428,36 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(feeFromRouter) ); if (feeSupplied < feeFromRouter) { vm.expectRevert(); - s_etherSenderReceiver.ccipSend{value: amount + feeSupplied}(destinationChainSelector, message); + s_etherSenderReceiver.ccipSend{value: AMOUNT + feeSupplied}(DESTINATION_CHAIN_SELECTOR, message); } else { bytes32 expectedMsgId = keccak256(abi.encode("ccip send")); vm.mockCall( ROUTER, feeSupplied, - abi.encodeWithSelector(IRouterClient.ccipSend.selector, destinationChainSelector, validatedMessage), + abi.encodeWithSelector(IRouterClient.ccipSend.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(expectedMsgId) ); bytes32 actualMsgId = - s_etherSenderReceiver.ccipSend{value: amount + feeSupplied}(destinationChainSelector, message); + s_etherSenderReceiver.ccipSend{value: AMOUNT + feeSupplied}(DESTINATION_CHAIN_SELECTOR, message); assertEq(actualMsgId, expectedMsgId, "message id must be correct"); } } function test_Fuzz_ccipSend_feeToken(uint256 feeFromRouter, uint256 feeSupplied) public { // cap the fuzzer because OWNER only has a million LINK. - vm.assume(feeSupplied < 1_000_000 ether - amount); + vm.assume(feeSupplied < 1_000_000 ether - AMOUNT); Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -477,7 +471,7 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(feeFromRouter) ); @@ -485,16 +479,16 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { if (feeSupplied < feeFromRouter) { vm.expectRevert(); - s_etherSenderReceiver.ccipSend{value: amount}(destinationChainSelector, message); + s_etherSenderReceiver.ccipSend{value: AMOUNT}(DESTINATION_CHAIN_SELECTOR, message); } else { bytes32 expectedMsgId = keccak256(abi.encode("ccip send")); vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.ccipSend.selector, destinationChainSelector, validatedMessage), + abi.encodeWithSelector(IRouterClient.ccipSend.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(expectedMsgId) ); - bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: amount}(destinationChainSelector, message); + bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: AMOUNT}(DESTINATION_CHAIN_SELECTOR, message); assertEq(actualMsgId, expectedMsgId, "message id must be correct"); } } @@ -503,7 +497,7 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -517,21 +511,21 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeWei) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_WEI) ); - s_weth.approve(address(s_etherSenderReceiver), feeWei - 1); + s_weth.approve(address(s_etherSenderReceiver), FEE_WEI - 1); vm.expectRevert("SafeERC20: low-level call failed"); - s_etherSenderReceiver.ccipSend{value: amount}(destinationChainSelector, message); + s_etherSenderReceiver.ccipSend{value: AMOUNT}(DESTINATION_CHAIN_SELECTOR, message); } function test_ccipSend_reverts_insufficientFee_feeToken() public { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -545,21 +539,21 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeJuels) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_JUELS) ); - s_linkToken.approve(address(s_etherSenderReceiver), feeJuels - 1); + s_linkToken.approve(address(s_etherSenderReceiver), FEE_JUELS - 1); vm.expectRevert("ERC20: insufficient allowance"); - s_etherSenderReceiver.ccipSend{value: amount}(destinationChainSelector, message); + s_etherSenderReceiver.ccipSend{value: AMOUNT}(DESTINATION_CHAIN_SELECTOR, message); } function test_ccipSend_reverts_insufficientFee_native() public { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -573,19 +567,19 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeWei) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_WEI) ); vm.expectRevert(); - s_etherSenderReceiver.ccipSend{value: amount + feeWei - 1}(destinationChainSelector, message); + s_etherSenderReceiver.ccipSend{value: AMOUNT + FEE_WEI - 1}(DESTINATION_CHAIN_SELECTOR, message); } function test_ccipSend_success_nativeExcess() public { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -600,20 +594,21 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { bytes32 expectedMsgId = keccak256(abi.encode("ccip send")); vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeWei) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_WEI) ); // we assert that the correct value is sent to the router call, which should be // the msg.value - feeWei. vm.mockCall( ROUTER, - feeWei + 1, - abi.encodeWithSelector(IRouterClient.ccipSend.selector, destinationChainSelector, validatedMessage), + FEE_WEI + 1, + abi.encodeWithSelector(IRouterClient.ccipSend.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(expectedMsgId) ); - bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: amount + feeWei + 1}(destinationChainSelector, message); + bytes32 actualMsgId = + s_etherSenderReceiver.ccipSend{value: AMOUNT + FEE_WEI + 1}(DESTINATION_CHAIN_SELECTOR, message); assertEq(actualMsgId, expectedMsgId, "message id must be correct"); } @@ -621,7 +616,7 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -636,17 +631,17 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { bytes32 expectedMsgId = keccak256(abi.encode("ccip send")); vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeWei) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_WEI) ); vm.mockCall( ROUTER, - feeWei, - abi.encodeWithSelector(IRouterClient.ccipSend.selector, destinationChainSelector, validatedMessage), + FEE_WEI, + abi.encodeWithSelector(IRouterClient.ccipSend.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(expectedMsgId) ); - bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: amount + feeWei}(destinationChainSelector, message); + bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: AMOUNT + FEE_WEI}(DESTINATION_CHAIN_SELECTOR, message); assertEq(actualMsgId, expectedMsgId, "message id must be correct"); } @@ -654,7 +649,7 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -669,28 +664,28 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { bytes32 expectedMsgId = keccak256(abi.encode("ccip send")); vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeJuels) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_JUELS) ); vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.ccipSend.selector, destinationChainSelector, validatedMessage), + abi.encodeWithSelector(IRouterClient.ccipSend.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(expectedMsgId) ); - s_linkToken.approve(address(s_etherSenderReceiver), feeJuels); + s_linkToken.approve(address(s_etherSenderReceiver), FEE_JUELS); - bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: amount}(destinationChainSelector, message); + bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: AMOUNT}(DESTINATION_CHAIN_SELECTOR, message); assertEq(actualMsgId, expectedMsgId, "message id must be correct"); uint256 routerAllowance = s_linkToken.allowance(address(s_etherSenderReceiver), ROUTER); - assertEq(routerAllowance, feeJuels, "router allowance must be feeJuels"); + assertEq(routerAllowance, FEE_JUELS, "router allowance must be feeJuels"); } function test_ccipSend_success_weth() public { Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1); tokenAmounts[0] = Client.EVMTokenAmount({ token: address(0), // callers may not specify this. - amount: amount + amount: AMOUNT }); Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(XCHAIN_RECEIVER), @@ -705,18 +700,18 @@ contract EtherSenderReceiverTest_ccipSend is EtherSenderReceiverTest { bytes32 expectedMsgId = keccak256(abi.encode("ccip send")); vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.getFee.selector, destinationChainSelector, validatedMessage), - abi.encode(feeWei) + abi.encodeWithSelector(IRouterClient.getFee.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), + abi.encode(FEE_WEI) ); vm.mockCall( ROUTER, - abi.encodeWithSelector(IRouterClient.ccipSend.selector, destinationChainSelector, validatedMessage), + abi.encodeWithSelector(IRouterClient.ccipSend.selector, DESTINATION_CHAIN_SELECTOR, validatedMessage), abi.encode(expectedMsgId) ); - s_weth.approve(address(s_etherSenderReceiver), feeWei); + s_weth.approve(address(s_etherSenderReceiver), FEE_WEI); - bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: amount}(destinationChainSelector, message); + bytes32 actualMsgId = s_etherSenderReceiver.ccipSend{value: AMOUNT}(DESTINATION_CHAIN_SELECTOR, message); assertEq(actualMsgId, expectedMsgId, "message id must be correct"); uint256 routerAllowance = s_weth.allowance(address(s_etherSenderReceiver), ROUTER); assertEq(routerAllowance, type(uint256).max, "router allowance must be max for weth"); diff --git a/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol b/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol index 61b0204e7d8..f3f09ecc78c 100644 --- a/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol +++ b/contracts/src/v0.8/ccip/test/applications/ImmutableExample.t.sol @@ -4,7 +4,7 @@ import {IAny2EVMMessageReceiver} from "../../interfaces/IAny2EVMMessageReceiver. import {CCIPClientExample} from "../../applications/CCIPClientExample.sol"; import {Client} from "../../libraries/Client.sol"; -import {OnRampSetup} from "../onRamp/OnRampSetup.t.sol"; +import {OnRampSetup} from "../onRamp/onRamp/OnRampSetup.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {ERC165Checker} from diff --git a/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol b/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol index d47ba1c54fb..f645bd88cb5 100644 --- a/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol +++ b/contracts/src/v0.8/ccip/test/applications/PingPongDemo.t.sol @@ -3,9 +3,12 @@ pragma solidity 0.8.24; import {PingPongDemo} from "../../applications/PingPongDemo.sol"; import {Client} from "../../libraries/Client.sol"; -import "../onRamp/OnRampSetup.t.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {OnRamp} from "../../onRamp/OnRamp.sol"; +import {OnRampSetup} from "../onRamp/onRamp/OnRampSetup.t.sol"; + +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -// setup contract PingPongDappSetup is OnRampSetup { PingPongDemo internal s_pingPong; IERC20 internal s_feeToken; @@ -27,7 +30,7 @@ contract PingPongDappSetup is OnRampSetup { } contract PingPong_startPingPong is PingPongDappSetup { - uint256 internal pingPongNumber = 1; + uint256 internal s_pingPongNumber = 1; function test_StartPingPong_With_Sequenced_Ordered_Success() public { _assertPingPongSuccess(); @@ -41,7 +44,7 @@ contract PingPong_startPingPong is PingPongDappSetup { function _assertPingPongSuccess() internal { vm.expectEmit(); - emit PingPongDemo.Ping(pingPongNumber); + emit PingPongDemo.Ping(s_pingPongNumber); Internal.EVM2AnyRampMessage memory message; diff --git a/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol b/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol index 0c1cc714be9..c50d86cad7d 100644 --- a/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol +++ b/contracts/src/v0.8/ccip/test/attacks/onRamp/OnRampTokenPoolReentrancy.t.sol @@ -2,10 +2,9 @@ pragma solidity 0.8.24; import {Client} from "../../../libraries/Client.sol"; -import {Internal} from "../../../libraries/Internal.sol"; import {OnRamp} from "../../../onRamp/OnRamp.sol"; import {TokenPool} from "../../../pools/TokenPool.sol"; -import {OnRampSetup} from "../../onRamp/OnRampSetup.t.sol"; +import {OnRampSetup} from "../../onRamp/onRamp/OnRampSetup.t.sol"; import {FacadeClient} from "./FacadeClient.sol"; import {ReentrantMaliciousTokenPool} from "./ReentrantMaliciousTokenPool.sol"; @@ -77,6 +76,7 @@ contract OnRampTokenPoolReentrancy is OnRampSetup { assertGt(expectedFee, 0); vm.expectRevert(OnRamp.ReentrancyGuardReentrantCall.selector); + // solhint-disable-next-line check-send-result s_facadeClient.send(amount); } } diff --git a/contracts/src/v0.8/ccip/test/capability/CCIPHome.t.sol b/contracts/src/v0.8/ccip/test/capability/CCIPHome.t.sol index d142e5fbfed..259506dd64a 100644 --- a/contracts/src/v0.8/ccip/test/capability/CCIPHome.t.sol +++ b/contracts/src/v0.8/ccip/test/capability/CCIPHome.t.sol @@ -8,7 +8,6 @@ import {CCIPHome} from "../../capability/CCIPHome.sol"; import {Internal} from "../../libraries/Internal.sol"; import {CCIPHomeHelper} from "../helpers/CCIPHomeHelper.sol"; import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; import {IERC165} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/interfaces/IERC165.sol"; diff --git a/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol b/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol index 83e567d965e..610bf311cd8 100644 --- a/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol +++ b/contracts/src/v0.8/ccip/test/e2e/End2End.t.sol @@ -6,20 +6,31 @@ import {IRMNRemote} from "../../interfaces/IRMNRemote.sol"; import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; import {NonceManager} from "../../NonceManager.sol"; +import {Router} from "../../Router.sol"; +import {Client} from "../../libraries/Client.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {OffRamp} from "../../offRamp/OffRamp.sol"; +import {OnRamp} from "../../onRamp/OnRamp.sol"; import {LockReleaseTokenPool} from "../../pools/LockReleaseTokenPool.sol"; import {TokenAdminRegistry} from "../../tokenAdminRegistry/TokenAdminRegistry.sol"; -import "../helpers/MerkleHelper.sol"; -import "../offRamp/OffRampSetup.t.sol"; -import "../onRamp/OnRampSetup.t.sol"; +import {MerkleHelper} from "../helpers/MerkleHelper.sol"; +import {OnRampHelper} from "../helpers/OnRampHelper.sol"; +import {OffRampSetup} from "../offRamp/offRamp/OffRampSetup.t.sol"; +import {OnRampSetup} from "../onRamp/onRamp/OnRampSetup.t.sol"; + +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; /// @notice This E2E test implements the following scenario: -/// 1. Send multiple messages from multiple source chains to a single destination chain (2 messages from source chain 1 and 1 from -/// source chain 2). +/// 1. Send multiple messages from multiple source chains to a single destination chain (2 messages from source chain +/// 1 and 1 from source chain 2). /// 2. Commit multiple merkle roots (1 for each source chain). /// 3. Batch execute all the committed messages. contract E2E is OnRampSetup, OffRampSetup { using Internal for Internal.Any2EVMRampMessage; + uint256 internal constant TOKEN_AMOUNT_1 = 9; + uint256 internal constant TOKEN_AMOUNT_2 = 7; + Router internal s_sourceRouter2; OnRampHelper internal s_onRamp2; TokenAdminRegistry internal s_tokenAdminRegistry2; @@ -129,9 +140,9 @@ contract E2E is OnRampSetup, OffRampSetup { uint256 expectedFee = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, _generateTokenMessage()); // Asserts that the tokens have been sent and the fee has been paid. assertEq( - balance0Pre - (messages1.length + messages2.length) * (i_tokenAmount0 + expectedFee), token0.balanceOf(OWNER) + balance0Pre - (messages1.length + messages2.length) * (TOKEN_AMOUNT_1 + expectedFee), token0.balanceOf(OWNER) ); - assertEq(balance1Pre - (messages1.length + messages2.length) * i_tokenAmount1, token1.balanceOf(OWNER)); + assertEq(balance1Pre - (messages1.length + messages2.length) * TOKEN_AMOUNT_2, token1.balanceOf(OWNER)); } // Commit @@ -207,7 +218,7 @@ contract E2E is OnRampSetup, OffRampSetup { vm.recordLogs(); _execute(reports); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR, messages1[0].header.sequenceNumber, messages1[0].header.messageId, @@ -216,7 +227,7 @@ contract E2E is OnRampSetup, OffRampSetup { "" ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR, messages1[1].header.sequenceNumber, messages1[1].header.messageId, @@ -225,7 +236,7 @@ contract E2E is OnRampSetup, OffRampSetup { "" ); - assertExecutionStateChangedEventLogs( + _assertExecutionStateChangedEventLogs( SOURCE_CHAIN_SELECTOR + 1, messages2[0].header.sequenceNumber, messages2[0].header.messageId, @@ -244,8 +255,8 @@ contract E2E is OnRampSetup, OffRampSetup { TokenAdminRegistry tokenAdminRegistry ) public returns (Internal.Any2EVMRampMessage memory) { Client.EVM2AnyMessage memory message = _generateTokenMessage(); - IERC20(s_sourceTokens[0]).approve(address(router), i_tokenAmount0 + router.getFee(DEST_CHAIN_SELECTOR, message)); - IERC20(s_sourceTokens[1]).approve(address(router), i_tokenAmount1); + IERC20(s_sourceTokens[0]).approve(address(router), TOKEN_AMOUNT_1 + router.getFee(DEST_CHAIN_SELECTOR, message)); + IERC20(s_sourceTokens[1]).approve(address(router), TOKEN_AMOUNT_2); uint256 feeAmount = router.getFee(DEST_CHAIN_SELECTOR, message); @@ -298,4 +309,17 @@ contract E2E is OnRampSetup, OffRampSetup { tokenAmounts: any2EVMTokenTransfer }); } + + function _generateTokenMessage() public view returns (Client.EVM2AnyMessage memory) { + Client.EVMTokenAmount[] memory tokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); + tokenAmounts[0].amount = TOKEN_AMOUNT_1; + tokenAmounts[1].amount = TOKEN_AMOUNT_2; + return Client.EVM2AnyMessage({ + receiver: abi.encode(OWNER), + data: "", + tokenAmounts: tokenAmounts, + feeToken: s_sourceFeeToken, + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) + }); + } } diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyDestChainConfigUpdates.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyDestChainConfigUpdates.t.sol new file mode 100644 index 00000000000..44fe0e33eba --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyDestChainConfigUpdates.t.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_applyDestChainConfigUpdates is FeeQuoterSetup { + function test_Fuzz_applyDestChainConfigUpdates_Success( + FeeQuoter.DestChainConfigArgs memory destChainConfigArgs + ) public { + vm.assume(destChainConfigArgs.destChainSelector != 0); + vm.assume(destChainConfigArgs.destChainConfig.maxPerMsgGasLimit != 0); + destChainConfigArgs.destChainConfig.defaultTxGasLimit = uint32( + bound( + destChainConfigArgs.destChainConfig.defaultTxGasLimit, 1, destChainConfigArgs.destChainConfig.maxPerMsgGasLimit + ) + ); + destChainConfigArgs.destChainConfig.chainFamilySelector = Internal.CHAIN_FAMILY_SELECTOR_EVM; + + bool isNewChain = destChainConfigArgs.destChainSelector != DEST_CHAIN_SELECTOR; + + FeeQuoter.DestChainConfigArgs[] memory newDestChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](1); + newDestChainConfigArgs[0] = destChainConfigArgs; + + if (isNewChain) { + vm.expectEmit(); + emit FeeQuoter.DestChainAdded(destChainConfigArgs.destChainSelector, destChainConfigArgs.destChainConfig); + } else { + vm.expectEmit(); + emit FeeQuoter.DestChainConfigUpdated(destChainConfigArgs.destChainSelector, destChainConfigArgs.destChainConfig); + } + + s_feeQuoter.applyDestChainConfigUpdates(newDestChainConfigArgs); + + _assertFeeQuoterDestChainConfigsEqual( + destChainConfigArgs.destChainConfig, s_feeQuoter.getDestChainConfig(destChainConfigArgs.destChainSelector) + ); + } + + function test_applyDestChainConfigUpdates_Success() public { + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](2); + destChainConfigArgs[0] = _generateFeeQuoterDestChainConfigArgs()[0]; + destChainConfigArgs[0].destChainConfig.isEnabled = false; + destChainConfigArgs[1] = _generateFeeQuoterDestChainConfigArgs()[0]; + destChainConfigArgs[1].destChainSelector = DEST_CHAIN_SELECTOR + 1; + + vm.expectEmit(); + emit FeeQuoter.DestChainConfigUpdated(DEST_CHAIN_SELECTOR, destChainConfigArgs[0].destChainConfig); + vm.expectEmit(); + emit FeeQuoter.DestChainAdded(DEST_CHAIN_SELECTOR + 1, destChainConfigArgs[1].destChainConfig); + + vm.recordLogs(); + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + FeeQuoter.DestChainConfig memory gotDestChainConfig0 = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + FeeQuoter.DestChainConfig memory gotDestChainConfig1 = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR + 1); + + assertEq(vm.getRecordedLogs().length, 2); + _assertFeeQuoterDestChainConfigsEqual(destChainConfigArgs[0].destChainConfig, gotDestChainConfig0); + _assertFeeQuoterDestChainConfigsEqual(destChainConfigArgs[1].destChainConfig, gotDestChainConfig1); + } + + function test_applyDestChainConfigUpdatesZeroInput_Success() public { + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](0); + + vm.recordLogs(); + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + assertEq(vm.getRecordedLogs().length, 0); + } + + // Reverts + + function test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero_Revert() public { + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; + + destChainConfigArg.destChainConfig.defaultTxGasLimit = 0; + vm.expectRevert( + abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) + ); + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + } + + function test_applyDestChainConfigUpdatesDefaultTxGasLimitGtMaxPerMessageGasLimit_Revert() public { + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; + + // Allow setting to the max value + destChainConfigArg.destChainConfig.defaultTxGasLimit = destChainConfigArg.destChainConfig.maxPerMsgGasLimit; + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + // Revert when exceeding max value + destChainConfigArg.destChainConfig.defaultTxGasLimit = destChainConfigArg.destChainConfig.maxPerMsgGasLimit + 1; + vm.expectRevert( + abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) + ); + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + } + + function test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() public { + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; + + destChainConfigArg.destChainSelector = 0; + vm.expectRevert( + abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) + ); + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + } + + function test_InvalidChainFamilySelector_Revert() public { + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; + + destChainConfigArg.destChainConfig.chainFamilySelector = bytes4(uint32(1)); + + vm.expectRevert( + abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) + ); + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyFeeTokensUpdates.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyFeeTokensUpdates.t.sol new file mode 100644 index 00000000000..a32e50bb3e4 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyFeeTokensUpdates.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_applyFeeTokensUpdates is FeeQuoterSetup { + function test_ApplyFeeTokensUpdates_Success() public { + address[] memory feeTokens = new address[](1); + feeTokens[0] = s_sourceTokens[1]; + + vm.expectEmit(); + emit FeeQuoter.FeeTokenAdded(feeTokens[0]); + + s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); + assertEq(s_feeQuoter.getFeeTokens().length, 3); + assertEq(s_feeQuoter.getFeeTokens()[2], feeTokens[0]); + + // add same feeToken is no-op + s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); + assertEq(s_feeQuoter.getFeeTokens().length, 3); + assertEq(s_feeQuoter.getFeeTokens()[2], feeTokens[0]); + + vm.expectEmit(); + emit FeeQuoter.FeeTokenRemoved(feeTokens[0]); + + s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); + assertEq(s_feeQuoter.getFeeTokens().length, 2); + + // removing already removed feeToken is no-op and does not emit an event + vm.recordLogs(); + + s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); + assertEq(s_feeQuoter.getFeeTokens().length, 2); + + vm.assertEq(vm.getRecordedLogs().length, 0); + + // Removing and adding the same fee token is allowed and emits both events + // Add it first + s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); + + vm.expectEmit(); + emit FeeQuoter.FeeTokenRemoved(feeTokens[0]); + vm.expectEmit(); + emit FeeQuoter.FeeTokenAdded(feeTokens[0]); + + s_feeQuoter.applyFeeTokensUpdates(feeTokens, feeTokens); + } + + function test_OnlyCallableByOwner_Revert() public { + vm.startPrank(STRANGER); + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + + s_feeQuoter.applyFeeTokensUpdates(new address[](0), new address[](0)); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyPremiumMultiplierWeiPerEthUpdates.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyPremiumMultiplierWeiPerEthUpdates.t.sol new file mode 100644 index 00000000000..d31202e8a99 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyPremiumMultiplierWeiPerEthUpdates.t.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates is FeeQuoterSetup { + function test_Fuzz_applyPremiumMultiplierWeiPerEthUpdates_Success( + FeeQuoter.PremiumMultiplierWeiPerEthArgs memory premiumMultiplierWeiPerEthArg + ) public { + FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs = + new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](1); + premiumMultiplierWeiPerEthArgs[0] = premiumMultiplierWeiPerEthArg; + + vm.expectEmit(); + emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( + premiumMultiplierWeiPerEthArg.token, premiumMultiplierWeiPerEthArg.premiumMultiplierWeiPerEth + ); + + s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); + + assertEq( + premiumMultiplierWeiPerEthArg.premiumMultiplierWeiPerEth, + s_feeQuoter.getPremiumMultiplierWeiPerEth(premiumMultiplierWeiPerEthArg.token) + ); + } + + function test_applyPremiumMultiplierWeiPerEthUpdatesSingleToken_Success() public { + FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs = + new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](1); + premiumMultiplierWeiPerEthArgs[0] = s_feeQuoterPremiumMultiplierWeiPerEthArgs[0]; + premiumMultiplierWeiPerEthArgs[0].token = vm.addr(1); + + vm.expectEmit(); + emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( + vm.addr(1), premiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth + ); + + s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); + + assertEq( + s_feeQuoterPremiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth, + s_feeQuoter.getPremiumMultiplierWeiPerEth(vm.addr(1)) + ); + } + + function test_applyPremiumMultiplierWeiPerEthUpdatesMultipleTokens_Success() public { + FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs = + new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](2); + premiumMultiplierWeiPerEthArgs[0] = s_feeQuoterPremiumMultiplierWeiPerEthArgs[0]; + premiumMultiplierWeiPerEthArgs[0].token = vm.addr(1); + premiumMultiplierWeiPerEthArgs[1].token = vm.addr(2); + + vm.expectEmit(); + emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( + vm.addr(1), premiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth + ); + vm.expectEmit(); + emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( + vm.addr(2), premiumMultiplierWeiPerEthArgs[1].premiumMultiplierWeiPerEth + ); + + s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); + + assertEq( + premiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth, + s_feeQuoter.getPremiumMultiplierWeiPerEth(vm.addr(1)) + ); + assertEq( + premiumMultiplierWeiPerEthArgs[1].premiumMultiplierWeiPerEth, + s_feeQuoter.getPremiumMultiplierWeiPerEth(vm.addr(2)) + ); + } + + function test_applyPremiumMultiplierWeiPerEthUpdatesZeroInput() public { + vm.recordLogs(); + s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](0)); + + assertEq(vm.getRecordedLogs().length, 0); + } + + // Reverts + + function test_OnlyCallableByOwnerOrAdmin_Revert() public { + FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs; + vm.startPrank(STRANGER); + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + + s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyTokenTransferFeeConfigUpdates.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyTokenTransferFeeConfigUpdates.t.sol new file mode 100644 index 00000000000..72ef7b89a75 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.applyTokenTransferFeeConfigUpdates.t.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Pool} from "../../libraries/Pool.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_applyTokenTransferFeeConfigUpdates is FeeQuoterSetup { + function test_Fuzz_ApplyTokenTransferFeeConfig_Success( + FeeQuoter.TokenTransferFeeConfig[2] memory tokenTransferFeeConfigs + ) public { + // To prevent Invalid Fee Range error from the fuzzer, bound the results to a valid range that + // where minFee < maxFee + tokenTransferFeeConfigs[0].minFeeUSDCents = + uint32(bound(tokenTransferFeeConfigs[0].minFeeUSDCents, 0, type(uint8).max)); + tokenTransferFeeConfigs[1].minFeeUSDCents = + uint32(bound(tokenTransferFeeConfigs[1].minFeeUSDCents, 0, type(uint8).max)); + + tokenTransferFeeConfigs[0].maxFeeUSDCents = uint32( + bound(tokenTransferFeeConfigs[0].maxFeeUSDCents, tokenTransferFeeConfigs[0].minFeeUSDCents + 1, type(uint32).max) + ); + tokenTransferFeeConfigs[1].maxFeeUSDCents = uint32( + bound(tokenTransferFeeConfigs[1].maxFeeUSDCents, tokenTransferFeeConfigs[1].minFeeUSDCents + 1, type(uint32).max) + ); + + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(2, 2); + tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + tokenTransferFeeConfigArgs[1].destChainSelector = DEST_CHAIN_SELECTOR + 1; + + for (uint256 i = 0; i < tokenTransferFeeConfigArgs.length; ++i) { + for (uint256 j = 0; j < tokenTransferFeeConfigs.length; ++j) { + tokenTransferFeeConfigs[j].destBytesOverhead = uint32( + bound(tokenTransferFeeConfigs[j].destBytesOverhead, Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, type(uint32).max) + ); + address feeToken = s_sourceTokens[j]; + tokenTransferFeeConfigArgs[i].tokenTransferFeeConfigs[j].token = feeToken; + tokenTransferFeeConfigArgs[i].tokenTransferFeeConfigs[j].tokenTransferFeeConfig = tokenTransferFeeConfigs[j]; + + vm.expectEmit(); + emit FeeQuoter.TokenTransferFeeConfigUpdated( + tokenTransferFeeConfigArgs[i].destChainSelector, feeToken, tokenTransferFeeConfigs[j] + ); + } + } + + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + + for (uint256 i = 0; i < tokenTransferFeeConfigs.length; ++i) { + _assertTokenTransferFeeConfigEqual( + tokenTransferFeeConfigs[i], + s_feeQuoter.getTokenTransferFeeConfig( + tokenTransferFeeConfigArgs[0].destChainSelector, + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[i].token + ) + ); + } + } + + function test_ApplyTokenTransferFeeConfig_Success() public { + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 2); + tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = address(5); + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ + minFeeUSDCents: 6, + maxFeeUSDCents: 7, + deciBps: 8, + destGasOverhead: 9, + destBytesOverhead: 312, + isEnabled: true + }); + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token = address(11); + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ + minFeeUSDCents: 12, + maxFeeUSDCents: 13, + deciBps: 14, + destGasOverhead: 15, + destBytesOverhead: 394, + isEnabled: true + }); + + vm.expectEmit(); + emit FeeQuoter.TokenTransferFeeConfigUpdated( + tokenTransferFeeConfigArgs[0].destChainSelector, + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token, + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig + ); + vm.expectEmit(); + emit FeeQuoter.TokenTransferFeeConfigUpdated( + tokenTransferFeeConfigArgs[0].destChainSelector, + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token, + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig + ); + + FeeQuoter.TokenTransferFeeConfigRemoveArgs[] memory tokensToRemove = + new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0); + s_feeQuoter.applyTokenTransferFeeConfigUpdates(tokenTransferFeeConfigArgs, tokensToRemove); + + FeeQuoter.TokenTransferFeeConfig memory config0 = s_feeQuoter.getTokenTransferFeeConfig( + tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token + ); + FeeQuoter.TokenTransferFeeConfig memory config1 = s_feeQuoter.getTokenTransferFeeConfig( + tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token + ); + + _assertTokenTransferFeeConfigEqual( + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig, config0 + ); + _assertTokenTransferFeeConfigEqual( + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig, config1 + ); + + // Remove only the first token and validate only the first token is removed + tokensToRemove = new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](1); + tokensToRemove[0] = FeeQuoter.TokenTransferFeeConfigRemoveArgs({ + destChainSelector: tokenTransferFeeConfigArgs[0].destChainSelector, + token: tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token + }); + + vm.expectEmit(); + emit FeeQuoter.TokenTransferFeeConfigDeleted( + tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token + ); + + s_feeQuoter.applyTokenTransferFeeConfigUpdates(new FeeQuoter.TokenTransferFeeConfigArgs[](0), tokensToRemove); + + config0 = s_feeQuoter.getTokenTransferFeeConfig( + tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token + ); + config1 = s_feeQuoter.getTokenTransferFeeConfig( + tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token + ); + + FeeQuoter.TokenTransferFeeConfig memory emptyConfig; + + _assertTokenTransferFeeConfigEqual(emptyConfig, config0); + _assertTokenTransferFeeConfigEqual( + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig, config1 + ); + } + + function test_ApplyTokenTransferFeeZeroInput() public { + vm.recordLogs(); + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + new FeeQuoter.TokenTransferFeeConfigArgs[](0), new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + + assertEq(vm.getRecordedLogs().length, 0); + } + + // Reverts + + function test_OnlyCallableByOwnerOrAdmin_Revert() public { + vm.startPrank(STRANGER); + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs; + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + } + + function test_InvalidDestBytesOverhead_Revert() public { + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); + tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = address(5); + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ + minFeeUSDCents: 6, + maxFeeUSDCents: 7, + deciBps: 8, + destGasOverhead: 9, + destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES - 1), + isEnabled: true + }); + + vm.expectRevert( + abi.encodeWithSelector( + FeeQuoter.InvalidDestBytesOverhead.selector, + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token, + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.destBytesOverhead + ) + ); + + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.constructor.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.constructor.t.sol new file mode 100644 index 00000000000..c2d36bee96d --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.constructor.t.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {FeeQuoterHelper} from "../helpers/FeeQuoterHelper.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_constructor is FeeQuoterSetup { + function test_Setup_Success() public virtual { + address[] memory priceUpdaters = new address[](2); + priceUpdaters[0] = STRANGER; + priceUpdaters[1] = OWNER; + address[] memory feeTokens = new address[](2); + feeTokens[0] = s_sourceTokens[0]; + feeTokens[1] = s_sourceTokens[1]; + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](2); + tokenPriceFeedUpdates[0] = + _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); + tokenPriceFeedUpdates[1] = + _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[1], s_dataFeedByToken[s_sourceTokens[1]], 6); + + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + + FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ + linkToken: s_sourceTokens[0], + maxFeeJuelsPerMsg: MAX_MSG_FEES_JUELS, + tokenPriceStalenessThreshold: uint32(TWELVE_HOURS) + }); + s_feeQuoter = new FeeQuoterHelper( + staticConfig, + priceUpdaters, + feeTokens, + tokenPriceFeedUpdates, + s_feeQuoterTokenTransferFeeConfigArgs, + s_feeQuoterPremiumMultiplierWeiPerEthArgs, + destChainConfigArgs + ); + + _assertFeeQuoterStaticConfigsEqual(s_feeQuoter.getStaticConfig(), staticConfig); + assertEq(feeTokens, s_feeQuoter.getFeeTokens()); + assertEq(priceUpdaters, s_feeQuoter.getAllAuthorizedCallers()); + assertEq(s_feeQuoter.typeAndVersion(), "FeeQuoter 1.6.0-dev"); + + _assertTokenPriceFeedConfigEquality( + tokenPriceFeedUpdates[0].feedConfig, s_feeQuoter.getTokenPriceFeedConfig(s_sourceTokens[0]) + ); + + _assertTokenPriceFeedConfigEquality( + tokenPriceFeedUpdates[1].feedConfig, s_feeQuoter.getTokenPriceFeedConfig(s_sourceTokens[1]) + ); + + assertEq( + s_feeQuoterPremiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth, + s_feeQuoter.getPremiumMultiplierWeiPerEth(s_feeQuoterPremiumMultiplierWeiPerEthArgs[0].token) + ); + + assertEq( + s_feeQuoterPremiumMultiplierWeiPerEthArgs[1].premiumMultiplierWeiPerEth, + s_feeQuoter.getPremiumMultiplierWeiPerEth(s_feeQuoterPremiumMultiplierWeiPerEthArgs[1].token) + ); + + FeeQuoter.TokenTransferFeeConfigArgs memory tokenTransferFeeConfigArg = s_feeQuoterTokenTransferFeeConfigArgs[0]; + for (uint256 i = 0; i < tokenTransferFeeConfigArg.tokenTransferFeeConfigs.length; ++i) { + FeeQuoter.TokenTransferFeeConfigSingleTokenArgs memory tokenFeeArgs = + s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[i]; + + _assertTokenTransferFeeConfigEqual( + tokenFeeArgs.tokenTransferFeeConfig, + s_feeQuoter.getTokenTransferFeeConfig(tokenTransferFeeConfigArg.destChainSelector, tokenFeeArgs.token) + ); + } + + for (uint256 i = 0; i < destChainConfigArgs.length; ++i) { + FeeQuoter.DestChainConfig memory expectedConfig = destChainConfigArgs[i].destChainConfig; + uint64 destChainSelector = destChainConfigArgs[i].destChainSelector; + + _assertFeeQuoterDestChainConfigsEqual(expectedConfig, s_feeQuoter.getDestChainConfig(destChainSelector)); + } + } + + function test_InvalidStalenessThreshold_Revert() public { + FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ + linkToken: s_sourceTokens[0], + maxFeeJuelsPerMsg: MAX_MSG_FEES_JUELS, + tokenPriceStalenessThreshold: 0 + }); + + vm.expectRevert(FeeQuoter.InvalidStaticConfig.selector); + + s_feeQuoter = new FeeQuoterHelper( + staticConfig, + new address[](0), + new address[](0), + new FeeQuoter.TokenPriceFeedUpdate[](0), + s_feeQuoterTokenTransferFeeConfigArgs, + s_feeQuoterPremiumMultiplierWeiPerEthArgs, + new FeeQuoter.DestChainConfigArgs[](0) + ); + } + + function test_InvalidLinkTokenEqZeroAddress_Revert() public { + FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ + linkToken: address(0), + maxFeeJuelsPerMsg: MAX_MSG_FEES_JUELS, + tokenPriceStalenessThreshold: uint32(TWELVE_HOURS) + }); + + vm.expectRevert(FeeQuoter.InvalidStaticConfig.selector); + + s_feeQuoter = new FeeQuoterHelper( + staticConfig, + new address[](0), + new address[](0), + new FeeQuoter.TokenPriceFeedUpdate[](0), + s_feeQuoterTokenTransferFeeConfigArgs, + s_feeQuoterPremiumMultiplierWeiPerEthArgs, + new FeeQuoter.DestChainConfigArgs[](0) + ); + } + + function test_InvalidMaxFeeJuelsPerMsg_Revert() public { + FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ + linkToken: s_sourceTokens[0], + maxFeeJuelsPerMsg: 0, + tokenPriceStalenessThreshold: uint32(TWELVE_HOURS) + }); + + vm.expectRevert(FeeQuoter.InvalidStaticConfig.selector); + + s_feeQuoter = new FeeQuoterHelper( + staticConfig, + new address[](0), + new address[](0), + new FeeQuoter.TokenPriceFeedUpdate[](0), + s_feeQuoterTokenTransferFeeConfigArgs, + s_feeQuoterPremiumMultiplierWeiPerEthArgs, + new FeeQuoter.DestChainConfigArgs[](0) + ); + } + + function _assertFeeQuoterStaticConfigsEqual( + FeeQuoter.StaticConfig memory a, + FeeQuoter.StaticConfig memory b + ) internal pure { + assertEq(a.linkToken, b.linkToken); + assertEq(a.maxFeeJuelsPerMsg, b.maxFeeJuelsPerMsg); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.convertTokenAmount.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.convertTokenAmount.t.sol new file mode 100644 index 00000000000..ca6a7e16126 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.convertTokenAmount.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_convertTokenAmount is FeeQuoterSetup { + function test_ConvertTokenAmount_Success() public view { + Internal.PriceUpdates memory initialPriceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); + uint256 amount = 3e16; + uint256 conversionRate = (uint256(initialPriceUpdates.tokenPriceUpdates[2].usdPerToken) * 1e18) + / uint256(initialPriceUpdates.tokenPriceUpdates[0].usdPerToken); + uint256 expected = (amount * conversionRate) / 1e18; + assertEq(s_feeQuoter.convertTokenAmount(s_weth, amount, s_sourceTokens[0]), expected); + } + + function test_Fuzz_ConvertTokenAmount_Success( + uint256 feeTokenAmount, + uint224 usdPerFeeToken, + uint160 usdPerLinkToken, + uint224 usdPerUnitGas + ) public { + vm.assume(usdPerFeeToken > 0); + vm.assume(usdPerLinkToken > 0); + // We bound the max fees to be at most uint96.max link. + feeTokenAmount = bound(feeTokenAmount, 0, (uint256(type(uint96).max) * usdPerLinkToken) / usdPerFeeToken); + + address feeToken = address(1); + address linkToken = address(2); + address[] memory feeTokens = new address[](1); + feeTokens[0] = feeToken; + s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); + + Internal.TokenPriceUpdate[] memory tokenPriceUpdates = new Internal.TokenPriceUpdate[](2); + tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: feeToken, usdPerToken: usdPerFeeToken}); + tokenPriceUpdates[1] = Internal.TokenPriceUpdate({sourceToken: linkToken, usdPerToken: usdPerLinkToken}); + + Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](1); + gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: DEST_CHAIN_SELECTOR, usdPerUnitGas: usdPerUnitGas}); + + Internal.PriceUpdates memory priceUpdates = + Internal.PriceUpdates({tokenPriceUpdates: tokenPriceUpdates, gasPriceUpdates: gasPriceUpdates}); + + s_feeQuoter.updatePrices(priceUpdates); + + uint256 linkFee = s_feeQuoter.convertTokenAmount(feeToken, feeTokenAmount, linkToken); + assertEq(linkFee, (feeTokenAmount * usdPerFeeToken) / usdPerLinkToken); + } + + // Reverts + + function test_LinkTokenNotSupported_Revert() public { + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, DUMMY_CONTRACT_ADDRESS)); + s_feeQuoter.convertTokenAmount(DUMMY_CONTRACT_ADDRESS, 3e16, s_sourceTokens[0]); + + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, DUMMY_CONTRACT_ADDRESS)); + s_feeQuoter.convertTokenAmount(s_sourceTokens[0], 3e16, DUMMY_CONTRACT_ADDRESS); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getDataAvailabilityCost.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getDataAvailabilityCost.t.sol new file mode 100644 index 00000000000..2e498746c3d --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getDataAvailabilityCost.t.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_getDataAvailabilityCost is FeeQuoterSetup { + function test_EmptyMessageCalculatesDataAvailabilityCost_Success() public { + uint256 dataAvailabilityCostUSD = + s_feeQuoter.getDataAvailabilityCost(DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, 0, 0, 0); + + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + + uint256 dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas + + destChainConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; + uint256 expectedDataAvailabilityCostUSD = + USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; + + assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); + + // Test that the cost is destination chain specific + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + destChainConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR + 1; + destChainConfigArgs[0].destChainConfig.destDataAvailabilityOverheadGas = + destChainConfig.destDataAvailabilityOverheadGas * 2; + destChainConfigArgs[0].destChainConfig.destGasPerDataAvailabilityByte = + destChainConfig.destGasPerDataAvailabilityByte * 2; + destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = + destChainConfig.destDataAvailabilityMultiplierBps * 2; + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR + 1); + uint256 dataAvailabilityCostUSD2 = + s_feeQuoter.getDataAvailabilityCost(DEST_CHAIN_SELECTOR + 1, USD_PER_DATA_AVAILABILITY_GAS, 0, 0, 0); + dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas + + destChainConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; + expectedDataAvailabilityCostUSD = + USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; + + assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD2); + assertFalse(dataAvailabilityCostUSD == dataAvailabilityCostUSD2); + } + + function test_SimpleMessageCalculatesDataAvailabilityCost_Success() public view { + uint256 dataAvailabilityCostUSD = + s_feeQuoter.getDataAvailabilityCost(DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, 100, 5, 50); + + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + + uint256 dataAvailabilityLengthBytes = + Internal.MESSAGE_FIXED_BYTES + 100 + (5 * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + 50; + uint256 dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas + + destChainConfig.destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; + uint256 expectedDataAvailabilityCostUSD = + USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; + + assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); + } + + function test_SimpleMessageCalculatesDataAvailabilityCostUnsupportedDestChainSelector_Success() public view { + uint256 dataAvailabilityCostUSD = s_feeQuoter.getDataAvailabilityCost(0, USD_PER_DATA_AVAILABILITY_GAS, 100, 5, 50); + + assertEq(dataAvailabilityCostUSD, 0); + } + + function test_Fuzz_ZeroDataAvailabilityGasPriceAlwaysCalculatesZeroDataAvailabilityCost_Success( + uint64 messageDataLength, + uint32 numberOfTokens, + uint32 tokenTransferBytesOverhead + ) public view { + uint256 dataAvailabilityCostUSD = s_feeQuoter.getDataAvailabilityCost( + DEST_CHAIN_SELECTOR, 0, messageDataLength, numberOfTokens, tokenTransferBytesOverhead + ); + + assertEq(0, dataAvailabilityCostUSD); + } + + function test_Fuzz_CalculateDataAvailabilityCost_Success( + uint64 destChainSelector, + uint32 destDataAvailabilityOverheadGas, + uint16 destGasPerDataAvailabilityByte, + uint16 destDataAvailabilityMultiplierBps, + uint112 dataAvailabilityGasPrice, + uint64 messageDataLength, + uint32 numberOfTokens, + uint32 tokenTransferBytesOverhead + ) public { + vm.assume(destChainSelector != 0); + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](1); + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(destChainSelector); + destChainConfigArgs[0] = + FeeQuoter.DestChainConfigArgs({destChainSelector: destChainSelector, destChainConfig: destChainConfig}); + destChainConfigArgs[0].destChainConfig.destDataAvailabilityOverheadGas = destDataAvailabilityOverheadGas; + destChainConfigArgs[0].destChainConfig.destGasPerDataAvailabilityByte = destGasPerDataAvailabilityByte; + destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = destDataAvailabilityMultiplierBps; + destChainConfigArgs[0].destChainConfig.defaultTxGasLimit = GAS_LIMIT; + destChainConfigArgs[0].destChainConfig.maxPerMsgGasLimit = GAS_LIMIT; + destChainConfigArgs[0].destChainConfig.chainFamilySelector = Internal.CHAIN_FAMILY_SELECTOR_EVM; + + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + uint256 dataAvailabilityCostUSD = s_feeQuoter.getDataAvailabilityCost( + destChainConfigArgs[0].destChainSelector, + dataAvailabilityGasPrice, + messageDataLength, + numberOfTokens, + tokenTransferBytesOverhead + ); + + uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength + + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; + + uint256 dataAvailabilityGas = + destDataAvailabilityOverheadGas + destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; + uint256 expectedDataAvailabilityCostUSD = + dataAvailabilityGasPrice * dataAvailabilityGas * destDataAvailabilityMultiplierBps * 1e14; + + assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenAndGasPrices.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenAndGasPrices.t.sol new file mode 100644 index 00000000000..18ccf5efa79 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenAndGasPrices.t.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_getTokenAndGasPrices is FeeQuoterSetup { + function test_GetFeeTokenAndGasPrices_Success() public view { + (uint224 feeTokenPrice, uint224 gasPrice) = s_feeQuoter.getTokenAndGasPrices(s_sourceFeeToken, DEST_CHAIN_SELECTOR); + + Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); + + assertEq(feeTokenPrice, s_sourceTokenPrices[0]); + assertEq(gasPrice, priceUpdates.gasPriceUpdates[0].usdPerUnitGas); + } + + function test_StalenessCheckDisabled_Success() public { + uint64 neverStaleChainSelector = 345678; + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + destChainConfigArgs[0].destChainSelector = neverStaleChainSelector; + destChainConfigArgs[0].destChainConfig.gasPriceStalenessThreshold = 0; // disables the staleness check + + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](1); + gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: neverStaleChainSelector, usdPerUnitGas: 999}); + + Internal.PriceUpdates memory priceUpdates = + Internal.PriceUpdates({tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), gasPriceUpdates: gasPriceUpdates}); + s_feeQuoter.updatePrices(priceUpdates); + + // this should have no affect! But we do it anyway to make sure the staleness check is disabled + vm.warp(block.timestamp + 52_000_000 weeks); // 1M-ish years + + (, uint224 gasPrice) = s_feeQuoter.getTokenAndGasPrices(s_sourceFeeToken, neverStaleChainSelector); + + assertEq(gasPrice, 999); + } + + function test_ZeroGasPrice_Success() public { + uint64 zeroGasDestChainSelector = 345678; + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + destChainConfigArgs[0].destChainSelector = zeroGasDestChainSelector; + + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](1); + gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: zeroGasDestChainSelector, usdPerUnitGas: 0}); + + Internal.PriceUpdates memory priceUpdates = + Internal.PriceUpdates({tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), gasPriceUpdates: gasPriceUpdates}); + s_feeQuoter.updatePrices(priceUpdates); + + (, uint224 gasPrice) = s_feeQuoter.getTokenAndGasPrices(s_sourceFeeToken, zeroGasDestChainSelector); + + assertEq(gasPrice, 0); + } + + function test_UnsupportedChain_Revert() public { + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.DestinationChainNotEnabled.selector, DEST_CHAIN_SELECTOR + 1)); + s_feeQuoter.getTokenAndGasPrices(s_sourceTokens[0], DEST_CHAIN_SELECTOR + 1); + } + + function test_StaleGasPrice_Revert() public { + uint256 diff = TWELVE_HOURS + 1; + vm.warp(block.timestamp + diff); + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.StaleGasPrice.selector, DEST_CHAIN_SELECTOR, TWELVE_HOURS, diff)); + s_feeQuoter.getTokenAndGasPrices(s_sourceTokens[0], DEST_CHAIN_SELECTOR); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrice.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrice.t.sol new file mode 100644 index 00000000000..c00e750d27f --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrice.t.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_getTokenPrice is FeeQuoterSetup { + function test_GetTokenPriceFromFeed_Success() public { + uint256 originalTimestampValue = block.timestamp; + + // Above staleness threshold + vm.warp(originalTimestampValue + s_feeQuoter.getStaticConfig().tokenPriceStalenessThreshold + 1); + + address sourceToken = _initialiseSingleTokenPriceFeed(); + + vm.expectCall(s_dataFeedByToken[sourceToken], abi.encodeWithSelector(MockV3Aggregator.latestRoundData.selector)); + + Internal.TimestampedPackedUint224 memory tokenPriceAnswer = s_feeQuoter.getTokenPrice(sourceToken); + + // Price answer is 1e8 (18 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 + assertEq(tokenPriceAnswer.value, uint224(1e18)); + assertEq(tokenPriceAnswer.timestamp, uint32(originalTimestampValue)); + } + + function test_GetTokenPrice_LocalMoreRecent_Success() public { + uint256 originalTimestampValue = block.timestamp; + + Internal.PriceUpdates memory update = Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), + gasPriceUpdates: new Internal.GasPriceUpdate[](0) + }); + + update.tokenPriceUpdates[0] = + Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: uint32(originalTimestampValue + 5)}); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated( + update.tokenPriceUpdates[0].sourceToken, update.tokenPriceUpdates[0].usdPerToken, block.timestamp + ); + + s_feeQuoter.updatePrices(update); + + vm.warp(originalTimestampValue + s_feeQuoter.getStaticConfig().tokenPriceStalenessThreshold + 10); + + Internal.TimestampedPackedUint224 memory tokenPriceAnswer = s_feeQuoter.getTokenPrice(s_sourceTokens[0]); + + //Assert that the returned price is the local price, not the oracle price + assertEq(tokenPriceAnswer.value, update.tokenPriceUpdates[0].usdPerToken); + assertEq(tokenPriceAnswer.timestamp, uint32(originalTimestampValue)); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrices.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrices.t.sol new file mode 100644 index 00000000000..63f936332fd --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenPrices.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_getTokenPrices is FeeQuoterSetup { + function test_GetTokenPrices_Success() public view { + Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); + + address[] memory tokens = new address[](3); + tokens[0] = s_sourceTokens[0]; + tokens[1] = s_sourceTokens[1]; + tokens[2] = s_weth; + + Internal.TimestampedPackedUint224[] memory tokenPrices = s_feeQuoter.getTokenPrices(tokens); + + assertEq(tokenPrices.length, 3); + assertEq(tokenPrices[0].value, priceUpdates.tokenPriceUpdates[0].usdPerToken); + assertEq(tokenPrices[1].value, priceUpdates.tokenPriceUpdates[1].usdPerToken); + assertEq(tokenPrices[2].value, priceUpdates.tokenPriceUpdates[2].usdPerToken); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenTransferCost.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenTransferCost.t.sol new file mode 100644 index 00000000000..a426775ca63 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getTokenTransferCost.t.sol @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Client} from "../../libraries/Client.sol"; +import {Pool} from "../../libraries/Pool.sol"; +import {USDPriceWith18Decimals} from "../../libraries/USDPriceWith18Decimals.sol"; +import {FeeQuoterFeeSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_getTokenTransferCost is FeeQuoterFeeSetup { + using USDPriceWith18Decimals for uint224; + + address internal s_selfServeTokenDefaultPricing = makeAddr("self-serve-token-default-pricing"); + + function test_NoTokenTransferChargesZeroFee_Success() public view { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + assertEq(0, feeUSDWei); + assertEq(0, destGasOverhead); + assertEq(0, destBytesOverhead); + } + + function test_getTokenTransferCost_selfServeUsesDefaults_Success() public view { + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_selfServeTokenDefaultPricing, 1000); + + // Get config to assert it isn't set + FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); + + assertFalse(transferFeeConfig.isEnabled); + + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + // Assert that the default values are used + assertEq(uint256(DEFAULT_TOKEN_FEE_USD_CENTS) * 1e16, feeUSDWei); + assertEq(DEFAULT_TOKEN_DEST_GAS_OVERHEAD, destGasOverhead); + assertEq(DEFAULT_TOKEN_BYTES_OVERHEAD, destBytesOverhead); + } + + function test_SmallTokenTransferChargesMinFeeAndGas_Success() public view { + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1000); + FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); + + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + assertEq(_configUSDCentToWei(transferFeeConfig.minFeeUSDCents), feeUSDWei); + assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); + assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); + } + + function test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() public view { + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 0); + FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); + + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + assertEq(_configUSDCentToWei(transferFeeConfig.minFeeUSDCents), feeUSDWei); + assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); + assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); + } + + function test_LargeTokenTransferChargesMaxFeeAndGas_Success() public view { + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1e36); + FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); + + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + assertEq(_configUSDCentToWei(transferFeeConfig.maxFeeUSDCents), feeUSDWei); + assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); + assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); + } + + function test_FeeTokenBpsFee_Success() public view { + uint256 tokenAmount = 10000e18; + + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); + FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); + + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + uint256 usdWei = _calcUSDValueFromTokenAmount(s_feeTokenPrice, tokenAmount); + uint256 bpsUSDWei = _applyBpsRatio( + usdWei, s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.deciBps + ); + + assertEq(bpsUSDWei, feeUSDWei); + assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); + assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); + } + + function test_CustomTokenBpsFee_Success() public view { + uint256 tokenAmount = 200000e18; + + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(OWNER), + data: "", + tokenAmounts: new Client.EVMTokenAmount[](1), + feeToken: s_sourceFeeToken, + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) + }); + message.tokenAmounts[0] = Client.EVMTokenAmount({token: CUSTOM_TOKEN, amount: tokenAmount}); + + FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); + + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + uint256 usdWei = _calcUSDValueFromTokenAmount(CUSTOM_TOKEN_PRICE, tokenAmount); + uint256 bpsUSDWei = _applyBpsRatio( + usdWei, s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig.deciBps + ); + + assertEq(bpsUSDWei, feeUSDWei); + assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); + assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); + } + + function test_ZeroFeeConfigChargesMinFee_Success() public { + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); + tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = s_sourceFeeToken; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ + minFeeUSDCents: 0, + maxFeeUSDCents: 1, + deciBps: 0, + destGasOverhead: 0, + destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES), + isEnabled: true + }); + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1e36); + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); + + // if token charges 0 bps, it should cost minFee to transfer + assertEq( + _configUSDCentToWei( + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.minFeeUSDCents + ), + feeUSDWei + ); + assertEq(0, destGasOverhead); + assertEq(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, destBytesOverhead); + } + + function test_Fuzz_TokenTransferFeeDuplicateTokens_Success(uint256 transfers, uint256 amount) public view { + // It shouldn't be possible to pay materially lower fees by splitting up the transfers. + // Note it is possible to pay higher fees since the minimum fees are added. + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + transfers = bound(transfers, 1, destChainConfig.maxNumberOfTokensPerMsg); + // Cap amount to avoid overflow + amount = bound(amount, 0, 1e36); + Client.EVMTokenAmount[] memory multiple = new Client.EVMTokenAmount[](transfers); + for (uint256 i = 0; i < transfers; ++i) { + multiple[i] = Client.EVMTokenAmount({token: s_sourceTokens[0], amount: amount}); + } + Client.EVMTokenAmount[] memory single = new Client.EVMTokenAmount[](1); + single[0] = Client.EVMTokenAmount({token: s_sourceTokens[0], amount: amount * transfers}); + + address feeToken = s_sourceRouter.getWrappedNative(); + + (uint256 feeSingleUSDWei, uint32 gasOverheadSingle, uint32 bytesOverheadSingle) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, feeToken, s_wrappedTokenPrice, single); + (uint256 feeMultipleUSDWei, uint32 gasOverheadMultiple, uint32 bytesOverheadMultiple) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, feeToken, s_wrappedTokenPrice, multiple); + + // Note that there can be a rounding error once per split. + assertGe(feeMultipleUSDWei, (feeSingleUSDWei - destChainConfig.maxNumberOfTokensPerMsg)); + assertEq(gasOverheadMultiple, gasOverheadSingle * transfers); + assertEq(bytesOverheadMultiple, bytesOverheadSingle * transfers); + } + + function test_MixedTokenTransferFee_Success() public view { + address[3] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative(), CUSTOM_TOKEN]; + uint224[3] memory tokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice, CUSTOM_TOKEN_PRICE]; + FeeQuoter.TokenTransferFeeConfig[3] memory tokenTransferFeeConfigs = [ + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[0]), + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[1]), + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[2]) + ]; + + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(OWNER), + data: "", + tokenAmounts: new Client.EVMTokenAmount[](3), + feeToken: s_sourceRouter.getWrappedNative(), + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) + }); + uint256 expectedTotalGas = 0; + uint256 expectedTotalBytes = 0; + + // Start with small token transfers, total bps fee is lower than min token transfer fee + for (uint256 i = 0; i < testTokens.length; ++i) { + message.tokenAmounts[i] = Client.EVMTokenAmount({token: testTokens[i], amount: 1e14}); + FeeQuoter.TokenTransferFeeConfig memory tokenTransferFeeConfig = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[i]); + + expectedTotalGas += tokenTransferFeeConfig.destGasOverhead == 0 + ? DEFAULT_TOKEN_DEST_GAS_OVERHEAD + : tokenTransferFeeConfig.destGasOverhead; + expectedTotalBytes += tokenTransferFeeConfig.destBytesOverhead == 0 + ? DEFAULT_TOKEN_BYTES_OVERHEAD + : tokenTransferFeeConfig.destBytesOverhead; + } + (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); + + uint256 expectedFeeUSDWei = 0; + for (uint256 i = 0; i < testTokens.length; ++i) { + expectedFeeUSDWei += _configUSDCentToWei( + tokenTransferFeeConfigs[i].minFeeUSDCents == 0 + ? DEFAULT_TOKEN_FEE_USD_CENTS + : tokenTransferFeeConfigs[i].minFeeUSDCents + ); + } + + assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 1"); + assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 1"); + assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 1"); + + // Set 1st token transfer to a meaningful amount so its bps fee is now between min and max fee + message.tokenAmounts[0] = Client.EVMTokenAmount({token: testTokens[0], amount: 10000e18}); + + uint256 token0USDWei = _applyBpsRatio( + _calcUSDValueFromTokenAmount(tokenPrices[0], message.tokenAmounts[0].amount), tokenTransferFeeConfigs[0].deciBps + ); + uint256 token1USDWei = _configUSDCentToWei(DEFAULT_TOKEN_FEE_USD_CENTS); + + (feeUSDWei, destGasOverhead, destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); + expectedFeeUSDWei = token0USDWei + token1USDWei + _configUSDCentToWei(tokenTransferFeeConfigs[2].minFeeUSDCents); + + assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 2"); + assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 2"); + assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 2"); + + // Set 2nd token transfer to a large amount that is higher than maxFeeUSD + message.tokenAmounts[2] = Client.EVMTokenAmount({token: testTokens[2], amount: 1e36}); + + (feeUSDWei, destGasOverhead, destBytesOverhead) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); + expectedFeeUSDWei = token0USDWei + token1USDWei + _configUSDCentToWei(tokenTransferFeeConfigs[2].maxFeeUSDCents); + + assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 3"); + assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 3"); + assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 3"); + } + + function _applyBpsRatio(uint256 tokenAmount, uint16 ratio) internal pure returns (uint256) { + return (tokenAmount * ratio) / 1e5; + } + + function _calcUSDValueFromTokenAmount(uint224 tokenPrice, uint256 tokenAmount) internal pure returns (uint256) { + return (tokenPrice * tokenAmount) / 1e18; + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedFee.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedFee.t.sol new file mode 100644 index 00000000000..fdafde91f62 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedFee.t.sol @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Client} from "../../libraries/Client.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {Pool} from "../../libraries/Pool.sol"; +import {USDPriceWith18Decimals} from "../../libraries/USDPriceWith18Decimals.sol"; +import {FeeQuoterFeeSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_getValidatedFee is FeeQuoterFeeSetup { + using USDPriceWith18Decimals for uint224; + + function test_EmptyMessage_Success() public view { + address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; + uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; + + for (uint256 i = 0; i < feeTokenPrices.length; ++i) { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.feeToken = testTokens[i]; + uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + + uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + + uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD; + uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); + uint256 messageFeeUSD = (_configUSDCentToWei(destChainConfig.networkFeeUSDCents) * premiumMultiplierWeiPerEth); + uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( + DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, 0 + ); + + uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; + assertEq(totalPriceInFeeToken, feeAmount); + } + } + + function test_ZeroDataAvailabilityMultiplier_Success() public { + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](1); + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + destChainConfigArgs[0] = + FeeQuoter.DestChainConfigArgs({destChainSelector: DEST_CHAIN_SELECTOR, destChainConfig: destChainConfig}); + destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = 0; + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); + + uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + + uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD; + uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); + uint256 messageFeeUSD = (_configUSDCentToWei(destChainConfig.networkFeeUSDCents) * premiumMultiplierWeiPerEth); + + uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD) / s_feeTokenPrice; + assertEq(totalPriceInFeeToken, feeAmount); + } + + function test_HighGasMessage_Success() public view { + address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; + uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; + + uint256 customGasLimit = MAX_GAS_LIMIT; + uint256 customDataSize = MAX_DATA_SIZE; + for (uint256 i = 0; i < feeTokenPrices.length; ++i) { + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(OWNER), + data: new bytes(customDataSize), + tokenAmounts: new Client.EVMTokenAmount[](0), + feeToken: testTokens[i], + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: customGasLimit})) + }); + + uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + + uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + uint256 gasUsed = customGasLimit + DEST_GAS_OVERHEAD + customDataSize * DEST_GAS_PER_PAYLOAD_BYTE; + uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); + uint256 messageFeeUSD = (_configUSDCentToWei(destChainConfig.networkFeeUSDCents) * premiumMultiplierWeiPerEth); + uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( + DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, 0 + ); + + uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; + assertEq(totalPriceInFeeToken, feeAmount); + } + } + + function test_SingleTokenMessage_Success() public view { + address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; + uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; + + uint256 tokenAmount = 10000e18; + for (uint256 i = 0; i < feeTokenPrices.length; ++i) { + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); + message.feeToken = testTokens[i]; + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + uint32 destBytesOverhead = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token).destBytesOverhead; + uint32 tokenBytesOverhead = + destBytesOverhead == 0 ? uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) : destBytesOverhead; + + uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + + uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD + + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token).destGasOverhead; + uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); + (uint256 transferFeeUSD,,) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, feeTokenPrices[i], message.tokenAmounts); + uint256 messageFeeUSD = (transferFeeUSD * s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken)); + uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( + DEST_CHAIN_SELECTOR, + USD_PER_DATA_AVAILABILITY_GAS, + message.data.length, + message.tokenAmounts.length, + tokenBytesOverhead + ); + + uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; + assertEq(totalPriceInFeeToken, feeAmount); + } + } + + function test_MessageWithDataAndTokenTransfer_Success() public view { + address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; + uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; + + uint256 customGasLimit = 1_000_000; + for (uint256 i = 0; i < feeTokenPrices.length; ++i) { + Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ + receiver: abi.encode(OWNER), + data: "", + tokenAmounts: new Client.EVMTokenAmount[](2), + feeToken: testTokens[i], + extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: customGasLimit})) + }); + uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); + FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); + + message.tokenAmounts[0] = Client.EVMTokenAmount({token: s_sourceFeeToken, amount: 10000e18}); // feeTokenAmount + message.tokenAmounts[1] = Client.EVMTokenAmount({token: CUSTOM_TOKEN, amount: 200000e18}); // customTokenAmount + message.data = "random bits and bytes that should be factored into the cost of the message"; + + uint32 tokenGasOverhead = 0; + uint32 tokenBytesOverhead = 0; + for (uint256 j = 0; j < message.tokenAmounts.length; ++j) { + tokenGasOverhead += + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[j].token).destGasOverhead; + uint32 destBytesOverhead = + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[j].token).destBytesOverhead; + tokenBytesOverhead += destBytesOverhead == 0 ? uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) : destBytesOverhead; + } + + uint256 gasUsed = + customGasLimit + DEST_GAS_OVERHEAD + message.data.length * DEST_GAS_PER_PAYLOAD_BYTE + tokenGasOverhead; + uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); + (uint256 transferFeeUSD,,) = + s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, feeTokenPrices[i], message.tokenAmounts); + uint256 messageFeeUSD = (transferFeeUSD * premiumMultiplierWeiPerEth); + uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( + DEST_CHAIN_SELECTOR, + USD_PER_DATA_AVAILABILITY_GAS, + message.data.length, + message.tokenAmounts.length, + tokenBytesOverhead + ); + + uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; + assertEq(totalPriceInFeeToken, s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message)); + } + } + + function test_Fuzz_EnforceOutOfOrder(bool enforce, bool allowOutOfOrderExecution) public { + // Update config to enforce allowOutOfOrderExecution = defaultVal. + vm.stopPrank(); + vm.startPrank(OWNER); + + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + destChainConfigArgs[0].destChainConfig.enforceOutOfOrder = enforce; + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = abi.encodeWithSelector( + Client.EVM_EXTRA_ARGS_V2_TAG, + Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: allowOutOfOrderExecution}) + ); + + // If enforcement is on, only true should be allowed. + if (enforce && !allowOutOfOrderExecution) { + vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); + } + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + } + + // Reverts + + function test_DestinationChainNotEnabled_Revert() public { + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.DestinationChainNotEnabled.selector, DEST_CHAIN_SELECTOR + 1)); + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR + 1, _generateEmptyMessage()); + } + + function test_EnforceOutOfOrder_Revert() public { + // Update config to enforce allowOutOfOrderExecution = true. + vm.stopPrank(); + vm.startPrank(OWNER); + + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + destChainConfigArgs[0].destChainConfig.enforceOutOfOrder = true; + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + vm.stopPrank(); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + // Empty extraArgs to should revert since it enforceOutOfOrder is true. + message.extraArgs = ""; + + vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + } + + function test_MessageTooLarge_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.data = new bytes(MAX_DATA_SIZE + 1); + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.MessageTooLarge.selector, MAX_DATA_SIZE, message.data.length)); + + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + } + + function test_TooManyTokens_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + uint256 tooMany = MAX_TOKENS_LENGTH + 1; + message.tokenAmounts = new Client.EVMTokenAmount[](tooMany); + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.UnsupportedNumberOfTokens.selector, tooMany, MAX_TOKENS_LENGTH)); + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + } + + // Asserts gasLimit must be <=maxGasLimit + function test_MessageGasLimitTooHigh_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: MAX_GAS_LIMIT + 1})); + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.MessageGasLimitTooHigh.selector)); + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + } + + function test_NotAFeeToken_Revert() public { + address notAFeeToken = address(0x111111); + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(notAFeeToken, 1); + message.feeToken = notAFeeToken; + + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.FeeTokenNotSupported.selector, notAFeeToken)); + + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + } + + function test_InvalidEVMAddress_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.receiver = abi.encode(type(uint208).max); + + vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, message.receiver)); + + s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedTokenPrice.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedTokenPrice.t.sol new file mode 100644 index 00000000000..6d508bc9116 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.getValidatedTokenPrice.t.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_getValidatedTokenPrice is FeeQuoterSetup { + function test_GetValidatedTokenPrice_Success() public view { + Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); + address token = priceUpdates.tokenPriceUpdates[0].sourceToken; + + uint224 tokenPrice = s_feeQuoter.getValidatedTokenPrice(token); + + assertEq(priceUpdates.tokenPriceUpdates[0].usdPerToken, tokenPrice); + } + + function test_GetValidatedTokenPriceFromFeed_Success() public { + uint256 originalTimestampValue = block.timestamp; + + // Right below staleness threshold + vm.warp(originalTimestampValue + TWELVE_HOURS); + + address sourceToken = _initialiseSingleTokenPriceFeed(); + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(sourceToken); + + // Price answer is 1e8 (18 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 + assertEq(tokenPriceAnswer, uint224(1e18)); + } + + function test_GetValidatedTokenPriceFromFeedOverStalenessPeriod_Success() public { + uint256 originalTimestampValue = block.timestamp; + + // Right above staleness threshold + vm.warp(originalTimestampValue + TWELVE_HOURS + 1); + + address sourceToken = _initialiseSingleTokenPriceFeed(); + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(sourceToken); + + // Price answer is 1e8 (18 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 + assertEq(tokenPriceAnswer, uint224(1e18)); + } + + function test_GetValidatedTokenPriceFromFeedMaxInt224Value_Success() public { + address tokenAddress = _deploySourceToken("testToken", 0, 18); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, int256(uint256(type(uint224).max))); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); + + // Price answer is: uint224.MAX_VALUE * (10 ** (36 - 18 - 18)) + assertEq(tokenPriceAnswer, uint224(type(uint224).max)); + } + + function test_GetValidatedTokenPriceFromFeedErc20Below18Decimals_Success() public { + address tokenAddress = _deploySourceToken("testToken", 0, 6); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 8, 1e8); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 6); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); + + // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e6) -> expected 1e30 + assertEq(tokenPriceAnswer, uint224(1e30)); + } + + function test_GetValidatedTokenPriceFromFeedErc20Above18Decimals_Success() public { + address tokenAddress = _deploySourceToken("testToken", 0, 24); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 8, 1e8); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 24); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); + + // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e24) -> expected 1e12 + assertEq(tokenPriceAnswer, uint224(1e12)); + } + + function test_GetValidatedTokenPriceFromFeedFeedAt18Decimals_Success() public { + address tokenAddress = _deploySourceToken("testToken", 0, 18); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, 1e18); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); + + // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 + assertEq(tokenPriceAnswer, uint224(1e18)); + } + + function test_GetValidatedTokenPriceFromFeedFeedAt0Decimals_Success() public { + address tokenAddress = _deploySourceToken("testToken", 0, 0); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 0, 1e31); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 0); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); + + // Price answer is 1e31 (0 decimal token) - unit is (1e18 * 1e18 / 1e0) -> expected 1e36 + assertEq(tokenPriceAnswer, uint224(1e67)); + } + + function test_GetValidatedTokenPriceFromFeedFlippedDecimals_Success() public { + address tokenAddress = _deploySourceToken("testToken", 0, 20); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 20, 1e18); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 20); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); + + // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e20) -> expected 1e14 + assertEq(tokenPriceAnswer, uint224(1e14)); + } + + function test_StaleFeeToken_Success() public { + vm.warp(block.timestamp + TWELVE_HOURS + 1); + + Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); + address token = priceUpdates.tokenPriceUpdates[0].sourceToken; + + uint224 tokenPrice = s_feeQuoter.getValidatedTokenPrice(token); + + assertEq(priceUpdates.tokenPriceUpdates[0].usdPerToken, tokenPrice); + } + + // Reverts + + function test_OverflowFeedPrice_Revert() public { + address tokenAddress = _deploySourceToken("testToken", 0, 18); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, int256(uint256(type(uint224).max) + 1)); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + vm.expectRevert(FeeQuoter.DataFeedValueOutOfUint224Range.selector); + s_feeQuoter.getValidatedTokenPrice(tokenAddress); + } + + function test_UnderflowFeedPrice_Revert() public { + address tokenAddress = _deploySourceToken("testToken", 0, 18); + address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, -1); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + vm.expectRevert(FeeQuoter.DataFeedValueOutOfUint224Range.selector); + s_feeQuoter.getValidatedTokenPrice(tokenAddress); + } + + function test_TokenNotSupported_Revert() public { + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, DUMMY_CONTRACT_ADDRESS)); + s_feeQuoter.getValidatedTokenPrice(DUMMY_CONTRACT_ADDRESS); + } + + function test_TokenNotSupportedFeed_Revert() public { + address sourceToken = _initialiseSingleTokenPriceFeed(); + MockV3Aggregator(s_dataFeedByToken[sourceToken]).updateAnswer(0); + Internal.PriceUpdates memory priceUpdates = Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), + gasPriceUpdates: new Internal.GasPriceUpdate[](0) + }); + priceUpdates.tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: sourceToken, usdPerToken: 0}); + + s_feeQuoter.updatePrices(priceUpdates); + + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, sourceToken)); + s_feeQuoter.getValidatedTokenPrice(sourceToken); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.onReport.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.onReport.t.sol new file mode 100644 index 00000000000..aba4e178865 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.onReport.t.sol @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {KeystoneFeedsPermissionHandler} from "../../../keystone/KeystoneFeedsPermissionHandler.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_onReport is FeeQuoterSetup { + address internal constant FORWARDER_1 = address(0x1); + address internal constant WORKFLOW_OWNER_1 = address(0x3); + bytes10 internal constant WORKFLOW_NAME_1 = "workflow1"; + bytes2 internal constant REPORT_NAME_1 = "01"; + address internal s_onReportTestToken1; + address internal s_onReportTestToken2; + + function setUp() public virtual override { + super.setUp(); + s_onReportTestToken1 = s_sourceTokens[0]; + s_onReportTestToken2 = _deploySourceToken("onReportTestToken2", 0, 20); + + KeystoneFeedsPermissionHandler.Permission[] memory permissions = new KeystoneFeedsPermissionHandler.Permission[](1); + permissions[0] = KeystoneFeedsPermissionHandler.Permission({ + forwarder: FORWARDER_1, + workflowOwner: WORKFLOW_OWNER_1, + workflowName: WORKFLOW_NAME_1, + reportName: REPORT_NAME_1, + isAllowed: true + }); + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeeds = new FeeQuoter.TokenPriceFeedUpdate[](2); + tokenPriceFeeds[0] = FeeQuoter.TokenPriceFeedUpdate({ + sourceToken: s_onReportTestToken1, + feedConfig: FeeQuoter.TokenPriceFeedConfig({dataFeedAddress: address(0x0), tokenDecimals: 18, isEnabled: true}) + }); + tokenPriceFeeds[1] = FeeQuoter.TokenPriceFeedUpdate({ + sourceToken: s_onReportTestToken2, + feedConfig: FeeQuoter.TokenPriceFeedConfig({dataFeedAddress: address(0x0), tokenDecimals: 20, isEnabled: true}) + }); + s_feeQuoter.setReportPermissions(permissions); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeeds); + } + + function test_onReport_Success() public { + bytes memory encodedPermissionsMetadata = + abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); + + FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](2); + report[0] = + FeeQuoter.ReceivedCCIPFeedReport({token: s_onReportTestToken1, price: 4e18, timestamp: uint32(block.timestamp)}); + report[1] = + FeeQuoter.ReceivedCCIPFeedReport({token: s_onReportTestToken2, price: 4e18, timestamp: uint32(block.timestamp)}); + + uint224 expectedStoredToken1Price = s_feeQuoter.calculateRebasedValue(18, 18, report[0].price); + uint224 expectedStoredToken2Price = s_feeQuoter.calculateRebasedValue(18, 20, report[1].price); + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_onReportTestToken1, expectedStoredToken1Price, block.timestamp); + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_onReportTestToken2, expectedStoredToken2Price, block.timestamp); + + changePrank(FORWARDER_1); + s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); + + vm.assertEq(s_feeQuoter.getTokenPrice(report[0].token).value, expectedStoredToken1Price); + vm.assertEq(s_feeQuoter.getTokenPrice(report[0].token).timestamp, report[0].timestamp); + + vm.assertEq(s_feeQuoter.getTokenPrice(report[1].token).value, expectedStoredToken2Price); + vm.assertEq(s_feeQuoter.getTokenPrice(report[1].token).timestamp, report[1].timestamp); + } + + function test_OnReport_StaleUpdate_SkipPriceUpdate_Success() public { + //Creating a correct report + bytes memory encodedPermissionsMetadata = + abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); + + FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); + report[0] = + FeeQuoter.ReceivedCCIPFeedReport({token: s_onReportTestToken1, price: 4e18, timestamp: uint32(block.timestamp)}); + + uint224 expectedStoredTokenPrice = s_feeQuoter.calculateRebasedValue(18, 18, report[0].price); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_onReportTestToken1, expectedStoredTokenPrice, block.timestamp); + + changePrank(FORWARDER_1); + //setting the correct price and time with the correct report + s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); + + //create a stale report + report[0] = FeeQuoter.ReceivedCCIPFeedReport({ + token: s_onReportTestToken1, + price: 4e18, + timestamp: uint32(block.timestamp - 1) + }); + + //record logs to check no events were emitted + vm.recordLogs(); + + s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); + + //no logs should have been emitted + assertEq(vm.getRecordedLogs().length, 0); + } + + function test_onReport_TokenNotSupported_Revert() public { + bytes memory encodedPermissionsMetadata = + abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); + FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); + report[0] = + FeeQuoter.ReceivedCCIPFeedReport({token: s_sourceTokens[1], price: 4e18, timestamp: uint32(block.timestamp)}); + + // Revert due to token config not being set with the isEnabled flag + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, s_sourceTokens[1])); + vm.startPrank(FORWARDER_1); + s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); + } + + function test_onReport_InvalidForwarder_Reverts() public { + bytes memory encodedPermissionsMetadata = + abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); + FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); + report[0] = + FeeQuoter.ReceivedCCIPFeedReport({token: s_sourceTokens[0], price: 4e18, timestamp: uint32(block.timestamp)}); + + vm.expectRevert( + abi.encodeWithSelector( + KeystoneFeedsPermissionHandler.ReportForwarderUnauthorized.selector, + STRANGER, + WORKFLOW_OWNER_1, + WORKFLOW_NAME_1, + REPORT_NAME_1 + ) + ); + changePrank(STRANGER); + s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); + } + + function test_onReport_UnsupportedToken_Reverts() public { + bytes memory encodedPermissionsMetadata = + abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); + FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); + report[0] = + FeeQuoter.ReceivedCCIPFeedReport({token: s_sourceTokens[1], price: 4e18, timestamp: uint32(block.timestamp)}); + + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, s_sourceTokens[1])); + changePrank(FORWARDER_1); + s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.parseEVMExtraArgsFromBytes.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.parseEVMExtraArgsFromBytes.t.sol new file mode 100644 index 00000000000..8f4e3f954ca --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.parseEVMExtraArgsFromBytes.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Client} from "../../libraries/Client.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_parseEVMExtraArgsFromBytes is FeeQuoterSetup { + FeeQuoter.DestChainConfig private s_destChainConfig; + + function setUp() public virtual override { + super.setUp(); + s_destChainConfig = _generateFeeQuoterDestChainConfigArgs()[0].destChainConfig; + } + + 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( + abi.encode(s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig)), + 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( + abi.encode(s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig)), abi.encode(inputArgs) + ); + } + + function test_EVMExtraArgsDefault_Success() public view { + Client.EVMExtraArgsV2 memory expectedOutputArgs = + Client.EVMExtraArgsV2({gasLimit: s_destChainConfig.defaultTxGasLimit, allowOutOfOrderExecution: false}); + + vm.assertEq( + abi.encode(s_feeQuoter.parseEVMExtraArgsFromBytes("", s_destChainConfig)), abi.encode(expectedOutputArgs) + ); + } + + // 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(FeeQuoter.InvalidExtraArgsTag.selector); + s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig); + } + + function test_EVMExtraArgsEnforceOutOfOrder_Revert() public { + Client.EVMExtraArgsV2 memory inputArgs = + Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT, allowOutOfOrderExecution: false}); + bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); + s_destChainConfig.enforceOutOfOrder = true; + + vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); + s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig); + } + + function test_EVMExtraArgsGasLimitTooHigh_Revert() public { + Client.EVMExtraArgsV2 memory inputArgs = + Client.EVMExtraArgsV2({gasLimit: s_destChainConfig.maxPerMsgGasLimit + 1, allowOutOfOrderExecution: true}); + bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); + + vm.expectRevert(FeeQuoter.MessageGasLimitTooHigh.selector); + s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.processMessageArgs.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.processMessageArgs.t.sol new file mode 100644 index 00000000000..65baa576ead --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.processMessageArgs.t.sol @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Client} from "../../libraries/Client.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {Pool} from "../../libraries/Pool.sol"; +import {USDPriceWith18Decimals} from "../../libraries/USDPriceWith18Decimals.sol"; +import {FeeQuoterFeeSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_processMessageArgs is FeeQuoterFeeSetup { + using USDPriceWith18Decimals for uint224; + + function setUp() public virtual override { + super.setUp(); + } + + function test_processMessageArgs_WithLinkTokenAmount_Success() public view { + ( + uint256 msgFeeJuels, + /* bool isOutOfOrderExecution */ + , + /* bytes memory convertedExtraArgs */ + , + /* destExecDataPerToken */ + ) = s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + // LINK + s_sourceTokens[0], + MAX_MSG_FEES_JUELS, + "", + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + + assertEq(msgFeeJuels, MAX_MSG_FEES_JUELS); + } + + function test_processMessageArgs_WithConvertedTokenAmount_Success() public view { + address feeToken = s_sourceTokens[1]; + uint256 feeTokenAmount = 10_000 gwei; + uint256 expectedConvertedAmount = s_feeQuoter.convertTokenAmount(feeToken, feeTokenAmount, s_sourceTokens[0]); + + ( + uint256 msgFeeJuels, + /* bool isOutOfOrderExecution */ + , + /* bytes memory convertedExtraArgs */ + , + /* destExecDataPerToken */ + ) = s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + feeToken, + feeTokenAmount, + "", + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + + assertEq(msgFeeJuels, expectedConvertedAmount); + } + + function test_processMessageArgs_WithEmptyEVMExtraArgs_Success() public view { + ( + /* uint256 msgFeeJuels */ + , + bool isOutOfOrderExecution, + bytes memory convertedExtraArgs, + /* destExecDataPerToken */ + ) = s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + s_sourceTokens[0], + 0, + "", + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + + assertEq(isOutOfOrderExecution, false); + assertEq(convertedExtraArgs, Client._argsToBytes(s_feeQuoter.parseEVMExtraArgsFromBytes("", DEST_CHAIN_SELECTOR))); + } + + function test_processMessageArgs_WithEVMExtraArgsV1_Success() public view { + bytes memory extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 1000})); + + ( + /* uint256 msgFeeJuels */ + , + bool isOutOfOrderExecution, + bytes memory convertedExtraArgs, + /* destExecDataPerToken */ + ) = s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + s_sourceTokens[0], + 0, + extraArgs, + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + + assertEq(isOutOfOrderExecution, false); + assertEq( + convertedExtraArgs, Client._argsToBytes(s_feeQuoter.parseEVMExtraArgsFromBytes(extraArgs, DEST_CHAIN_SELECTOR)) + ); + } + + function test_processMessageArgs_WitEVMExtraArgsV2_Success() public view { + bytes memory extraArgs = Client._argsToBytes(Client.EVMExtraArgsV2({gasLimit: 0, allowOutOfOrderExecution: true})); + + ( + /* uint256 msgFeeJuels */ + , + bool isOutOfOrderExecution, + bytes memory convertedExtraArgs, + /* destExecDataPerToken */ + ) = s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + s_sourceTokens[0], + 0, + extraArgs, + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + + assertEq(isOutOfOrderExecution, true); + assertEq( + convertedExtraArgs, Client._argsToBytes(s_feeQuoter.parseEVMExtraArgsFromBytes(extraArgs, DEST_CHAIN_SELECTOR)) + ); + } + + // Reverts + + function test_processMessageArgs_MessageFeeTooHigh_Revert() public { + vm.expectRevert( + abi.encodeWithSelector(FeeQuoter.MessageFeeTooHigh.selector, MAX_MSG_FEES_JUELS + 1, MAX_MSG_FEES_JUELS) + ); + + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + s_sourceTokens[0], + MAX_MSG_FEES_JUELS + 1, + "", + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + } + + function test_processMessageArgs_InvalidExtraArgs_Revert() public { + vm.expectRevert(FeeQuoter.InvalidExtraArgsTag.selector); + + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + s_sourceTokens[0], + 0, + "wrong extra args", + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + } + + function test_processMessageArgs_MalformedEVMExtraArgs_Revert() public { + // abi.decode error + vm.expectRevert(); + + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + s_sourceTokens[0], + 0, + abi.encodeWithSelector(Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV1({gasLimit: 100})), + new Internal.EVM2AnyTokenTransfer[](0), + new Client.EVMTokenAmount[](0) + ); + } + + function test_processMessageArgs_WithCorrectPoolReturnData_Success() public view { + Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](2); + sourceTokenAmounts[0].amount = 1e18; + sourceTokenAmounts[0].token = s_sourceTokens[0]; + sourceTokenAmounts[1].amount = 1e18; + sourceTokenAmounts[1].token = CUSTOM_TOKEN_2; + + Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](2); + tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); + tokenAmounts[1] = _getSourceTokenData(sourceTokenAmounts[1], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); + bytes[] memory expectedDestExecData = new bytes[](2); + expectedDestExecData[0] = abi.encode( + s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.destGasOverhead + ); + expectedDestExecData[1] = abi.encode(DEFAULT_TOKEN_DEST_GAS_OVERHEAD); //expected return data should be abi.encoded default as isEnabled is false + + // No revert - successful + ( /* msgFeeJuels */ , /* isOutOfOrderExecution */, /* convertedExtraArgs */, bytes[] memory destExecData) = + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts + ); + + for (uint256 i = 0; i < destExecData.length; ++i) { + assertEq(destExecData[i], expectedDestExecData[i]); + } + } + + function test_processMessageArgs_TokenAmountArraysMismatching_Revert() public { + Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](2); + sourceTokenAmounts[0].amount = 1e18; + sourceTokenAmounts[0].token = s_sourceTokens[0]; + + Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](1); + tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); + + // Revert due to index out of bounds access + vm.expectRevert(); + + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, + s_sourceTokens[0], + MAX_MSG_FEES_JUELS, + "", + new Internal.EVM2AnyTokenTransfer[](1), + new Client.EVMTokenAmount[](0) + ); + } + + function test_applyTokensTransferFeeConfigUpdates_InvalidFeeRange_Revert() public { + address sourceETH = s_sourceTokens[1]; + + // Set token config to allow larger data + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); + tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = sourceETH; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ + minFeeUSDCents: 1, + maxFeeUSDCents: 0, + deciBps: 0, + destGasOverhead: 0, + destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, + isEnabled: true + }); + + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.InvalidFeeRange.selector, 1, 0)); + + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + } + + function test_processMessageArgs_SourceTokenDataTooLarge_Revert() public { + address sourceETH = s_sourceTokens[1]; + + Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](1); + sourceTokenAmounts[0].amount = 1000; + sourceTokenAmounts[0].token = sourceETH; + + Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](1); + tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); + + // No data set, should succeed + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts + ); + + // Set max data length, should succeed + tokenAmounts[0].extraData = new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES); + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts + ); + + // Set data to max length +1, should revert + tokenAmounts[0].extraData = new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + 1); + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts + ); + + // Set token config to allow larger data + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); + tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = sourceETH; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ + minFeeUSDCents: 0, + maxFeeUSDCents: 1, + deciBps: 0, + destGasOverhead: 0, + destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, + isEnabled: true + }); + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts + ); + + // Set the token data larger than the configured token data, should revert + tokenAmounts[0].extraData = new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + 32 + 1); + + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts + ); + } + + function test_processMessageArgs_InvalidEVMAddressDestToken_Revert() public { + bytes memory nonEvmAddress = abi.encode(type(uint208).max); + + Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](1); + sourceTokenAmounts[0].amount = 1e18; + sourceTokenAmounts[0].token = s_sourceTokens[0]; + + Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](1); + tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); + tokenAmounts[0].destTokenAddress = nonEvmAddress; + + vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, nonEvmAddress)); + s_feeQuoter.processMessageArgs( + DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.t.sol deleted file mode 100644 index c622312a7ec..00000000000 --- a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.t.sol +++ /dev/null @@ -1,2375 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {KeystoneFeedsPermissionHandler} from "../../../keystone/KeystoneFeedsPermissionHandler.sol"; -import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; -import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; -import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol"; -import {FeeQuoter} from "../../FeeQuoter.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {USDPriceWith18Decimals} from "../../libraries/USDPriceWith18Decimals.sol"; -import {FeeQuoterHelper} from "../helpers/FeeQuoterHelper.sol"; -import {FeeQuoterFeeSetup, FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; - -import {Vm} from "forge-std/Vm.sol"; - -contract FeeQuoter_constructor is FeeQuoterSetup { - function test_Setup_Success() public virtual { - address[] memory priceUpdaters = new address[](2); - priceUpdaters[0] = STRANGER; - priceUpdaters[1] = OWNER; - address[] memory feeTokens = new address[](2); - feeTokens[0] = s_sourceTokens[0]; - feeTokens[1] = s_sourceTokens[1]; - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](2); - tokenPriceFeedUpdates[0] = - _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); - tokenPriceFeedUpdates[1] = - _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[1], s_dataFeedByToken[s_sourceTokens[1]], 6); - - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - - FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ - linkToken: s_sourceTokens[0], - maxFeeJuelsPerMsg: MAX_MSG_FEES_JUELS, - tokenPriceStalenessThreshold: uint32(TWELVE_HOURS) - }); - s_feeQuoter = new FeeQuoterHelper( - staticConfig, - priceUpdaters, - feeTokens, - tokenPriceFeedUpdates, - s_feeQuoterTokenTransferFeeConfigArgs, - s_feeQuoterPremiumMultiplierWeiPerEthArgs, - destChainConfigArgs - ); - - _assertFeeQuoterStaticConfigsEqual(s_feeQuoter.getStaticConfig(), staticConfig); - assertEq(feeTokens, s_feeQuoter.getFeeTokens()); - assertEq(priceUpdaters, s_feeQuoter.getAllAuthorizedCallers()); - assertEq(s_feeQuoter.typeAndVersion(), "FeeQuoter 1.6.0-dev"); - - _assertTokenPriceFeedConfigEquality( - tokenPriceFeedUpdates[0].feedConfig, s_feeQuoter.getTokenPriceFeedConfig(s_sourceTokens[0]) - ); - - _assertTokenPriceFeedConfigEquality( - tokenPriceFeedUpdates[1].feedConfig, s_feeQuoter.getTokenPriceFeedConfig(s_sourceTokens[1]) - ); - - assertEq( - s_feeQuoterPremiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth, - s_feeQuoter.getPremiumMultiplierWeiPerEth(s_feeQuoterPremiumMultiplierWeiPerEthArgs[0].token) - ); - - assertEq( - s_feeQuoterPremiumMultiplierWeiPerEthArgs[1].premiumMultiplierWeiPerEth, - s_feeQuoter.getPremiumMultiplierWeiPerEth(s_feeQuoterPremiumMultiplierWeiPerEthArgs[1].token) - ); - - FeeQuoter.TokenTransferFeeConfigArgs memory tokenTransferFeeConfigArg = s_feeQuoterTokenTransferFeeConfigArgs[0]; - for (uint256 i = 0; i < tokenTransferFeeConfigArg.tokenTransferFeeConfigs.length; ++i) { - FeeQuoter.TokenTransferFeeConfigSingleTokenArgs memory tokenFeeArgs = - s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[i]; - - _assertTokenTransferFeeConfigEqual( - tokenFeeArgs.tokenTransferFeeConfig, - s_feeQuoter.getTokenTransferFeeConfig(tokenTransferFeeConfigArg.destChainSelector, tokenFeeArgs.token) - ); - } - - for (uint256 i = 0; i < destChainConfigArgs.length; ++i) { - FeeQuoter.DestChainConfig memory expectedConfig = destChainConfigArgs[i].destChainConfig; - uint64 destChainSelector = destChainConfigArgs[i].destChainSelector; - - _assertFeeQuoterDestChainConfigsEqual(expectedConfig, s_feeQuoter.getDestChainConfig(destChainSelector)); - } - } - - function test_InvalidStalenessThreshold_Revert() public { - FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ - linkToken: s_sourceTokens[0], - maxFeeJuelsPerMsg: MAX_MSG_FEES_JUELS, - tokenPriceStalenessThreshold: 0 - }); - - vm.expectRevert(FeeQuoter.InvalidStaticConfig.selector); - - s_feeQuoter = new FeeQuoterHelper( - staticConfig, - new address[](0), - new address[](0), - new FeeQuoter.TokenPriceFeedUpdate[](0), - s_feeQuoterTokenTransferFeeConfigArgs, - s_feeQuoterPremiumMultiplierWeiPerEthArgs, - new FeeQuoter.DestChainConfigArgs[](0) - ); - } - - function test_InvalidLinkTokenEqZeroAddress_Revert() public { - FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ - linkToken: address(0), - maxFeeJuelsPerMsg: MAX_MSG_FEES_JUELS, - tokenPriceStalenessThreshold: uint32(TWELVE_HOURS) - }); - - vm.expectRevert(FeeQuoter.InvalidStaticConfig.selector); - - s_feeQuoter = new FeeQuoterHelper( - staticConfig, - new address[](0), - new address[](0), - new FeeQuoter.TokenPriceFeedUpdate[](0), - s_feeQuoterTokenTransferFeeConfigArgs, - s_feeQuoterPremiumMultiplierWeiPerEthArgs, - new FeeQuoter.DestChainConfigArgs[](0) - ); - } - - function test_InvalidMaxFeeJuelsPerMsg_Revert() public { - FeeQuoter.StaticConfig memory staticConfig = FeeQuoter.StaticConfig({ - linkToken: s_sourceTokens[0], - maxFeeJuelsPerMsg: 0, - tokenPriceStalenessThreshold: uint32(TWELVE_HOURS) - }); - - vm.expectRevert(FeeQuoter.InvalidStaticConfig.selector); - - s_feeQuoter = new FeeQuoterHelper( - staticConfig, - new address[](0), - new address[](0), - new FeeQuoter.TokenPriceFeedUpdate[](0), - s_feeQuoterTokenTransferFeeConfigArgs, - s_feeQuoterPremiumMultiplierWeiPerEthArgs, - new FeeQuoter.DestChainConfigArgs[](0) - ); - } -} - -contract FeeQuoter_getTokenPrices is FeeQuoterSetup { - function test_GetTokenPrices_Success() public view { - Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); - - address[] memory tokens = new address[](3); - tokens[0] = s_sourceTokens[0]; - tokens[1] = s_sourceTokens[1]; - tokens[2] = s_weth; - - Internal.TimestampedPackedUint224[] memory tokenPrices = s_feeQuoter.getTokenPrices(tokens); - - assertEq(tokenPrices.length, 3); - assertEq(tokenPrices[0].value, priceUpdates.tokenPriceUpdates[0].usdPerToken); - assertEq(tokenPrices[1].value, priceUpdates.tokenPriceUpdates[1].usdPerToken); - assertEq(tokenPrices[2].value, priceUpdates.tokenPriceUpdates[2].usdPerToken); - } -} - -contract FeeQuoter_getTokenPrice is FeeQuoterSetup { - function test_GetTokenPriceFromFeed_Success() public { - uint256 originalTimestampValue = block.timestamp; - - // Above staleness threshold - vm.warp(originalTimestampValue + s_feeQuoter.getStaticConfig().tokenPriceStalenessThreshold + 1); - - address sourceToken = _initialiseSingleTokenPriceFeed(); - - vm.expectCall(s_dataFeedByToken[sourceToken], abi.encodeWithSelector(MockV3Aggregator.latestRoundData.selector)); - - Internal.TimestampedPackedUint224 memory tokenPriceAnswer = s_feeQuoter.getTokenPrice(sourceToken); - - // Price answer is 1e8 (18 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 - assertEq(tokenPriceAnswer.value, uint224(1e18)); - assertEq(tokenPriceAnswer.timestamp, uint32(originalTimestampValue)); - } - - function test_GetTokenPrice_LocalMoreRecent_Success() public { - uint256 originalTimestampValue = block.timestamp; - - Internal.PriceUpdates memory update = Internal.PriceUpdates({ - tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), - gasPriceUpdates: new Internal.GasPriceUpdate[](0) - }); - - update.tokenPriceUpdates[0] = - Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: uint32(originalTimestampValue + 5)}); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated( - update.tokenPriceUpdates[0].sourceToken, update.tokenPriceUpdates[0].usdPerToken, block.timestamp - ); - - s_feeQuoter.updatePrices(update); - - vm.warp(originalTimestampValue + s_feeQuoter.getStaticConfig().tokenPriceStalenessThreshold + 10); - - Internal.TimestampedPackedUint224 memory tokenPriceAnswer = s_feeQuoter.getTokenPrice(s_sourceTokens[0]); - - //Assert that the returned price is the local price, not the oracle price - assertEq(tokenPriceAnswer.value, update.tokenPriceUpdates[0].usdPerToken); - assertEq(tokenPriceAnswer.timestamp, uint32(originalTimestampValue)); - } -} - -contract FeeQuoter_getValidatedTokenPrice is FeeQuoterSetup { - function test_GetValidatedTokenPrice_Success() public view { - Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); - address token = priceUpdates.tokenPriceUpdates[0].sourceToken; - - uint224 tokenPrice = s_feeQuoter.getValidatedTokenPrice(token); - - assertEq(priceUpdates.tokenPriceUpdates[0].usdPerToken, tokenPrice); - } - - function test_GetValidatedTokenPriceFromFeed_Success() public { - uint256 originalTimestampValue = block.timestamp; - - // Right below staleness threshold - vm.warp(originalTimestampValue + TWELVE_HOURS); - - address sourceToken = _initialiseSingleTokenPriceFeed(); - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(sourceToken); - - // Price answer is 1e8 (18 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 - assertEq(tokenPriceAnswer, uint224(1e18)); - } - - function test_GetValidatedTokenPriceFromFeedOverStalenessPeriod_Success() public { - uint256 originalTimestampValue = block.timestamp; - - // Right above staleness threshold - vm.warp(originalTimestampValue + TWELVE_HOURS + 1); - - address sourceToken = _initialiseSingleTokenPriceFeed(); - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(sourceToken); - - // Price answer is 1e8 (18 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 - assertEq(tokenPriceAnswer, uint224(1e18)); - } - - function test_GetValidatedTokenPriceFromFeedMaxInt224Value_Success() public { - address tokenAddress = _deploySourceToken("testToken", 0, 18); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, int256(uint256(type(uint224).max))); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); - - // Price answer is: uint224.MAX_VALUE * (10 ** (36 - 18 - 18)) - assertEq(tokenPriceAnswer, uint224(type(uint224).max)); - } - - function test_GetValidatedTokenPriceFromFeedErc20Below18Decimals_Success() public { - address tokenAddress = _deploySourceToken("testToken", 0, 6); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 8, 1e8); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 6); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); - - // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e6) -> expected 1e30 - assertEq(tokenPriceAnswer, uint224(1e30)); - } - - function test_GetValidatedTokenPriceFromFeedErc20Above18Decimals_Success() public { - address tokenAddress = _deploySourceToken("testToken", 0, 24); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 8, 1e8); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 24); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); - - // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e24) -> expected 1e12 - assertEq(tokenPriceAnswer, uint224(1e12)); - } - - function test_GetValidatedTokenPriceFromFeedFeedAt18Decimals_Success() public { - address tokenAddress = _deploySourceToken("testToken", 0, 18); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, 1e18); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); - - // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e18) -> expected 1e18 - assertEq(tokenPriceAnswer, uint224(1e18)); - } - - function test_GetValidatedTokenPriceFromFeedFeedAt0Decimals_Success() public { - address tokenAddress = _deploySourceToken("testToken", 0, 0); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 0, 1e31); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 0); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); - - // Price answer is 1e31 (0 decimal token) - unit is (1e18 * 1e18 / 1e0) -> expected 1e36 - assertEq(tokenPriceAnswer, uint224(1e67)); - } - - function test_GetValidatedTokenPriceFromFeedFlippedDecimals_Success() public { - address tokenAddress = _deploySourceToken("testToken", 0, 20); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 20, 1e18); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 20); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - uint224 tokenPriceAnswer = s_feeQuoter.getValidatedTokenPrice(tokenAddress); - - // Price answer is 1e8 (6 decimal token) - unit is (1e18 * 1e18 / 1e20) -> expected 1e14 - assertEq(tokenPriceAnswer, uint224(1e14)); - } - - function test_StaleFeeToken_Success() public { - vm.warp(block.timestamp + TWELVE_HOURS + 1); - - Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); - address token = priceUpdates.tokenPriceUpdates[0].sourceToken; - - uint224 tokenPrice = s_feeQuoter.getValidatedTokenPrice(token); - - assertEq(priceUpdates.tokenPriceUpdates[0].usdPerToken, tokenPrice); - } - - // Reverts - - function test_OverflowFeedPrice_Revert() public { - address tokenAddress = _deploySourceToken("testToken", 0, 18); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, int256(uint256(type(uint224).max) + 1)); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - vm.expectRevert(FeeQuoter.DataFeedValueOutOfUint224Range.selector); - s_feeQuoter.getValidatedTokenPrice(tokenAddress); - } - - function test_UnderflowFeedPrice_Revert() public { - address tokenAddress = _deploySourceToken("testToken", 0, 18); - address feedAddress = _deployTokenPriceDataFeed(tokenAddress, 18, -1); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = _getSingleTokenPriceFeedUpdateStruct(tokenAddress, feedAddress, 18); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - vm.expectRevert(FeeQuoter.DataFeedValueOutOfUint224Range.selector); - s_feeQuoter.getValidatedTokenPrice(tokenAddress); - } - - function test_TokenNotSupported_Revert() public { - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, DUMMY_CONTRACT_ADDRESS)); - s_feeQuoter.getValidatedTokenPrice(DUMMY_CONTRACT_ADDRESS); - } - - function test_TokenNotSupportedFeed_Revert() public { - address sourceToken = _initialiseSingleTokenPriceFeed(); - MockV3Aggregator(s_dataFeedByToken[sourceToken]).updateAnswer(0); - Internal.PriceUpdates memory priceUpdates = Internal.PriceUpdates({ - tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), - gasPriceUpdates: new Internal.GasPriceUpdate[](0) - }); - priceUpdates.tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: sourceToken, usdPerToken: 0}); - - s_feeQuoter.updatePrices(priceUpdates); - - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, sourceToken)); - s_feeQuoter.getValidatedTokenPrice(sourceToken); - } -} - -contract FeeQuoter_applyFeeTokensUpdates is FeeQuoterSetup { - function test_ApplyFeeTokensUpdates_Success() public { - address[] memory feeTokens = new address[](1); - feeTokens[0] = s_sourceTokens[1]; - - vm.expectEmit(); - emit FeeQuoter.FeeTokenAdded(feeTokens[0]); - - s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); - assertEq(s_feeQuoter.getFeeTokens().length, 3); - assertEq(s_feeQuoter.getFeeTokens()[2], feeTokens[0]); - - // add same feeToken is no-op - s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); - assertEq(s_feeQuoter.getFeeTokens().length, 3); - assertEq(s_feeQuoter.getFeeTokens()[2], feeTokens[0]); - - vm.expectEmit(); - emit FeeQuoter.FeeTokenRemoved(feeTokens[0]); - - s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); - assertEq(s_feeQuoter.getFeeTokens().length, 2); - - // removing already removed feeToken is no-op and does not emit an event - vm.recordLogs(); - - s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); - assertEq(s_feeQuoter.getFeeTokens().length, 2); - - vm.assertEq(vm.getRecordedLogs().length, 0); - - // Removing and adding the same fee token is allowed and emits both events - // Add it first - s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); - - vm.expectEmit(); - emit FeeQuoter.FeeTokenRemoved(feeTokens[0]); - vm.expectEmit(); - emit FeeQuoter.FeeTokenAdded(feeTokens[0]); - - s_feeQuoter.applyFeeTokensUpdates(feeTokens, feeTokens); - } - - function test_OnlyCallableByOwner_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - - s_feeQuoter.applyFeeTokensUpdates(new address[](0), new address[](0)); - } -} - -contract FeeQuoter_updatePrices is FeeQuoterSetup { - function test_OnlyTokenPrice_Success() public { - Internal.PriceUpdates memory update = Internal.PriceUpdates({ - tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), - gasPriceUpdates: new Internal.GasPriceUpdate[](0) - }); - update.tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: 4e18}); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated( - update.tokenPriceUpdates[0].sourceToken, update.tokenPriceUpdates[0].usdPerToken, block.timestamp - ); - - s_feeQuoter.updatePrices(update); - - assertEq(s_feeQuoter.getTokenPrice(s_sourceTokens[0]).value, update.tokenPriceUpdates[0].usdPerToken); - } - - function test_OnlyGasPrice_Success() public { - Internal.PriceUpdates memory update = Internal.PriceUpdates({ - tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), - gasPriceUpdates: new Internal.GasPriceUpdate[](1) - }); - update.gasPriceUpdates[0] = - Internal.GasPriceUpdate({destChainSelector: DEST_CHAIN_SELECTOR, usdPerUnitGas: 2000e18}); - - vm.expectEmit(); - emit FeeQuoter.UsdPerUnitGasUpdated( - update.gasPriceUpdates[0].destChainSelector, update.gasPriceUpdates[0].usdPerUnitGas, block.timestamp - ); - - s_feeQuoter.updatePrices(update); - - assertEq( - s_feeQuoter.getDestinationChainGasPrice(DEST_CHAIN_SELECTOR).value, update.gasPriceUpdates[0].usdPerUnitGas - ); - } - - function test_UpdateMultiplePrices_Success() public { - Internal.TokenPriceUpdate[] memory tokenPriceUpdates = new Internal.TokenPriceUpdate[](3); - tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: 4e18}); - tokenPriceUpdates[1] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[1], usdPerToken: 1800e18}); - tokenPriceUpdates[2] = Internal.TokenPriceUpdate({sourceToken: address(12345), usdPerToken: 1e18}); - - Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](3); - gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: DEST_CHAIN_SELECTOR, usdPerUnitGas: 2e6}); - gasPriceUpdates[1] = Internal.GasPriceUpdate({destChainSelector: SOURCE_CHAIN_SELECTOR, usdPerUnitGas: 2000e18}); - gasPriceUpdates[2] = Internal.GasPriceUpdate({destChainSelector: 12345, usdPerUnitGas: 1e18}); - - Internal.PriceUpdates memory update = - Internal.PriceUpdates({tokenPriceUpdates: tokenPriceUpdates, gasPriceUpdates: gasPriceUpdates}); - - for (uint256 i = 0; i < tokenPriceUpdates.length; ++i) { - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated( - update.tokenPriceUpdates[i].sourceToken, update.tokenPriceUpdates[i].usdPerToken, block.timestamp - ); - } - for (uint256 i = 0; i < gasPriceUpdates.length; ++i) { - vm.expectEmit(); - emit FeeQuoter.UsdPerUnitGasUpdated( - update.gasPriceUpdates[i].destChainSelector, update.gasPriceUpdates[i].usdPerUnitGas, block.timestamp - ); - } - - s_feeQuoter.updatePrices(update); - - for (uint256 i = 0; i < tokenPriceUpdates.length; ++i) { - assertEq( - s_feeQuoter.getTokenPrice(update.tokenPriceUpdates[i].sourceToken).value, tokenPriceUpdates[i].usdPerToken - ); - } - for (uint256 i = 0; i < gasPriceUpdates.length; ++i) { - assertEq( - s_feeQuoter.getDestinationChainGasPrice(update.gasPriceUpdates[i].destChainSelector).value, - gasPriceUpdates[i].usdPerUnitGas - ); - } - } - - function test_UpdatableByAuthorizedCaller_Success() public { - Internal.PriceUpdates memory priceUpdates = Internal.PriceUpdates({ - tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), - gasPriceUpdates: new Internal.GasPriceUpdate[](0) - }); - priceUpdates.tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: 4e18}); - - // Revert when caller is not authorized - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(AuthorizedCallers.UnauthorizedCaller.selector, STRANGER)); - s_feeQuoter.updatePrices(priceUpdates); - - address[] memory priceUpdaters = new address[](1); - priceUpdaters[0] = STRANGER; - vm.startPrank(OWNER); - s_feeQuoter.applyAuthorizedCallerUpdates( - AuthorizedCallers.AuthorizedCallerArgs({addedCallers: priceUpdaters, removedCallers: new address[](0)}) - ); - - // Stranger is now an authorized caller to update prices - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated( - priceUpdates.tokenPriceUpdates[0].sourceToken, priceUpdates.tokenPriceUpdates[0].usdPerToken, block.timestamp - ); - s_feeQuoter.updatePrices(priceUpdates); - - assertEq(s_feeQuoter.getTokenPrice(s_sourceTokens[0]).value, priceUpdates.tokenPriceUpdates[0].usdPerToken); - - vm.startPrank(OWNER); - s_feeQuoter.applyAuthorizedCallerUpdates( - AuthorizedCallers.AuthorizedCallerArgs({addedCallers: new address[](0), removedCallers: priceUpdaters}) - ); - - // Revert when authorized caller is removed - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(AuthorizedCallers.UnauthorizedCaller.selector, STRANGER)); - s_feeQuoter.updatePrices(priceUpdates); - } - - // Reverts - - function test_OnlyCallableByUpdater_Revert() public { - Internal.PriceUpdates memory priceUpdates = Internal.PriceUpdates({ - tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), - gasPriceUpdates: new Internal.GasPriceUpdate[](0) - }); - - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(AuthorizedCallers.UnauthorizedCaller.selector, STRANGER)); - s_feeQuoter.updatePrices(priceUpdates); - } -} - -contract FeeQuoter_convertTokenAmount is FeeQuoterSetup { - function test_ConvertTokenAmount_Success() public view { - Internal.PriceUpdates memory initialPriceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); - uint256 amount = 3e16; - uint256 conversionRate = (uint256(initialPriceUpdates.tokenPriceUpdates[2].usdPerToken) * 1e18) - / uint256(initialPriceUpdates.tokenPriceUpdates[0].usdPerToken); - uint256 expected = (amount * conversionRate) / 1e18; - assertEq(s_feeQuoter.convertTokenAmount(s_weth, amount, s_sourceTokens[0]), expected); - } - - function test_Fuzz_ConvertTokenAmount_Success( - uint256 feeTokenAmount, - uint224 usdPerFeeToken, - uint160 usdPerLinkToken, - uint224 usdPerUnitGas - ) public { - vm.assume(usdPerFeeToken > 0); - vm.assume(usdPerLinkToken > 0); - // We bound the max fees to be at most uint96.max link. - feeTokenAmount = bound(feeTokenAmount, 0, (uint256(type(uint96).max) * usdPerLinkToken) / usdPerFeeToken); - - address feeToken = address(1); - address linkToken = address(2); - address[] memory feeTokens = new address[](1); - feeTokens[0] = feeToken; - s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); - - Internal.TokenPriceUpdate[] memory tokenPriceUpdates = new Internal.TokenPriceUpdate[](2); - tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: feeToken, usdPerToken: usdPerFeeToken}); - tokenPriceUpdates[1] = Internal.TokenPriceUpdate({sourceToken: linkToken, usdPerToken: usdPerLinkToken}); - - Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](1); - gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: DEST_CHAIN_SELECTOR, usdPerUnitGas: usdPerUnitGas}); - - Internal.PriceUpdates memory priceUpdates = - Internal.PriceUpdates({tokenPriceUpdates: tokenPriceUpdates, gasPriceUpdates: gasPriceUpdates}); - - s_feeQuoter.updatePrices(priceUpdates); - - uint256 linkFee = s_feeQuoter.convertTokenAmount(feeToken, feeTokenAmount, linkToken); - assertEq(linkFee, (feeTokenAmount * usdPerFeeToken) / usdPerLinkToken); - } - - // Reverts - - function test_LinkTokenNotSupported_Revert() public { - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, DUMMY_CONTRACT_ADDRESS)); - s_feeQuoter.convertTokenAmount(DUMMY_CONTRACT_ADDRESS, 3e16, s_sourceTokens[0]); - - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, DUMMY_CONTRACT_ADDRESS)); - s_feeQuoter.convertTokenAmount(s_sourceTokens[0], 3e16, DUMMY_CONTRACT_ADDRESS); - } -} - -contract FeeQuoter_getTokenAndGasPrices is FeeQuoterSetup { - function test_GetFeeTokenAndGasPrices_Success() public view { - (uint224 feeTokenPrice, uint224 gasPrice) = s_feeQuoter.getTokenAndGasPrices(s_sourceFeeToken, DEST_CHAIN_SELECTOR); - - Internal.PriceUpdates memory priceUpdates = abi.decode(s_encodedInitialPriceUpdates, (Internal.PriceUpdates)); - - assertEq(feeTokenPrice, s_sourceTokenPrices[0]); - assertEq(gasPrice, priceUpdates.gasPriceUpdates[0].usdPerUnitGas); - } - - function test_StalenessCheckDisabled_Success() public { - uint64 neverStaleChainSelector = 345678; - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - destChainConfigArgs[0].destChainSelector = neverStaleChainSelector; - destChainConfigArgs[0].destChainConfig.gasPriceStalenessThreshold = 0; // disables the staleness check - - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](1); - gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: neverStaleChainSelector, usdPerUnitGas: 999}); - - Internal.PriceUpdates memory priceUpdates = - Internal.PriceUpdates({tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), gasPriceUpdates: gasPriceUpdates}); - s_feeQuoter.updatePrices(priceUpdates); - - // this should have no affect! But we do it anyway to make sure the staleness check is disabled - vm.warp(block.timestamp + 52_000_000 weeks); // 1M-ish years - - (, uint224 gasPrice) = s_feeQuoter.getTokenAndGasPrices(s_sourceFeeToken, neverStaleChainSelector); - - assertEq(gasPrice, 999); - } - - function test_ZeroGasPrice_Success() public { - uint64 zeroGasDestChainSelector = 345678; - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - destChainConfigArgs[0].destChainSelector = zeroGasDestChainSelector; - - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](1); - gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: zeroGasDestChainSelector, usdPerUnitGas: 0}); - - Internal.PriceUpdates memory priceUpdates = - Internal.PriceUpdates({tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), gasPriceUpdates: gasPriceUpdates}); - s_feeQuoter.updatePrices(priceUpdates); - - (, uint224 gasPrice) = s_feeQuoter.getTokenAndGasPrices(s_sourceFeeToken, zeroGasDestChainSelector); - - assertEq(gasPrice, 0); - } - - function test_UnsupportedChain_Revert() public { - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.DestinationChainNotEnabled.selector, DEST_CHAIN_SELECTOR + 1)); - s_feeQuoter.getTokenAndGasPrices(s_sourceTokens[0], DEST_CHAIN_SELECTOR + 1); - } - - function test_StaleGasPrice_Revert() public { - uint256 diff = TWELVE_HOURS + 1; - vm.warp(block.timestamp + diff); - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.StaleGasPrice.selector, DEST_CHAIN_SELECTOR, TWELVE_HOURS, diff)); - s_feeQuoter.getTokenAndGasPrices(s_sourceTokens[0], DEST_CHAIN_SELECTOR); - } -} - -contract FeeQuoter_updateTokenPriceFeeds is FeeQuoterSetup { - function test_ZeroFeeds_Success() public { - Vm.Log[] memory logEntries = vm.getRecordedLogs(); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](0); - vm.recordLogs(); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - // Verify no log emissions - assertEq(logEntries.length, 0); - } - - function test_SingleFeedUpdate_Success() public { - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = - _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); - - _assertTokenPriceFeedConfigNotConfigured(s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken)); - - vm.expectEmit(); - emit FeeQuoter.PriceFeedPerTokenUpdated(tokenPriceFeedUpdates[0].sourceToken, tokenPriceFeedUpdates[0].feedConfig); - - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - _assertTokenPriceFeedConfigEquality( - s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig - ); - } - - function test_MultipleFeedUpdate_Success() public { - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](2); - - for (uint256 i = 0; i < 2; ++i) { - tokenPriceFeedUpdates[i] = - _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[i], s_dataFeedByToken[s_sourceTokens[i]], 18); - - _assertTokenPriceFeedConfigNotConfigured( - s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[i].sourceToken) - ); - - vm.expectEmit(); - emit FeeQuoter.PriceFeedPerTokenUpdated(tokenPriceFeedUpdates[i].sourceToken, tokenPriceFeedUpdates[i].feedConfig); - } - - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - _assertTokenPriceFeedConfigEquality( - s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig - ); - _assertTokenPriceFeedConfigEquality( - s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[1].sourceToken), tokenPriceFeedUpdates[1].feedConfig - ); - } - - function test_FeedUnset_Success() public { - Internal.TimestampedPackedUint224 memory priceQueryInitial = s_feeQuoter.getTokenPrice(s_sourceTokens[0]); - assertFalse(priceQueryInitial.value == 0); - assertFalse(priceQueryInitial.timestamp == 0); - - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = - _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); - - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - _assertTokenPriceFeedConfigEquality( - s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig - ); - - tokenPriceFeedUpdates[0].feedConfig.dataFeedAddress = address(0); - vm.expectEmit(); - emit FeeQuoter.PriceFeedPerTokenUpdated(tokenPriceFeedUpdates[0].sourceToken, tokenPriceFeedUpdates[0].feedConfig); - - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - _assertTokenPriceFeedConfigEquality( - s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig - ); - - // Price data should remain after a feed has been set->unset - Internal.TimestampedPackedUint224 memory priceQueryPostUnsetFeed = s_feeQuoter.getTokenPrice(s_sourceTokens[0]); - assertEq(priceQueryPostUnsetFeed.value, priceQueryInitial.value); - assertEq(priceQueryPostUnsetFeed.timestamp, priceQueryInitial.timestamp); - } - - function test_FeedNotUpdated() public { - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = - _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); - - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - - _assertTokenPriceFeedConfigEquality( - s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig - ); - } - - // Reverts - - function test_FeedUpdatedByNonOwner_Revert() public { - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); - tokenPriceFeedUpdates[0] = - _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); - - vm.startPrank(STRANGER); - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); - } -} - -contract FeeQuoter_applyDestChainConfigUpdates is FeeQuoterSetup { - function test_Fuzz_applyDestChainConfigUpdates_Success( - FeeQuoter.DestChainConfigArgs memory destChainConfigArgs - ) public { - vm.assume(destChainConfigArgs.destChainSelector != 0); - vm.assume(destChainConfigArgs.destChainConfig.maxPerMsgGasLimit != 0); - destChainConfigArgs.destChainConfig.defaultTxGasLimit = uint32( - bound( - destChainConfigArgs.destChainConfig.defaultTxGasLimit, 1, destChainConfigArgs.destChainConfig.maxPerMsgGasLimit - ) - ); - destChainConfigArgs.destChainConfig.chainFamilySelector = Internal.CHAIN_FAMILY_SELECTOR_EVM; - - bool isNewChain = destChainConfigArgs.destChainSelector != DEST_CHAIN_SELECTOR; - - FeeQuoter.DestChainConfigArgs[] memory newDestChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](1); - newDestChainConfigArgs[0] = destChainConfigArgs; - - if (isNewChain) { - vm.expectEmit(); - emit FeeQuoter.DestChainAdded(destChainConfigArgs.destChainSelector, destChainConfigArgs.destChainConfig); - } else { - vm.expectEmit(); - emit FeeQuoter.DestChainConfigUpdated(destChainConfigArgs.destChainSelector, destChainConfigArgs.destChainConfig); - } - - s_feeQuoter.applyDestChainConfigUpdates(newDestChainConfigArgs); - - _assertFeeQuoterDestChainConfigsEqual( - destChainConfigArgs.destChainConfig, s_feeQuoter.getDestChainConfig(destChainConfigArgs.destChainSelector) - ); - } - - function test_applyDestChainConfigUpdates_Success() public { - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](2); - destChainConfigArgs[0] = _generateFeeQuoterDestChainConfigArgs()[0]; - destChainConfigArgs[0].destChainConfig.isEnabled = false; - destChainConfigArgs[1] = _generateFeeQuoterDestChainConfigArgs()[0]; - destChainConfigArgs[1].destChainSelector = DEST_CHAIN_SELECTOR + 1; - - vm.expectEmit(); - emit FeeQuoter.DestChainConfigUpdated(DEST_CHAIN_SELECTOR, destChainConfigArgs[0].destChainConfig); - vm.expectEmit(); - emit FeeQuoter.DestChainAdded(DEST_CHAIN_SELECTOR + 1, destChainConfigArgs[1].destChainConfig); - - vm.recordLogs(); - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - FeeQuoter.DestChainConfig memory gotDestChainConfig0 = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - FeeQuoter.DestChainConfig memory gotDestChainConfig1 = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR + 1); - - assertEq(vm.getRecordedLogs().length, 2); - _assertFeeQuoterDestChainConfigsEqual(destChainConfigArgs[0].destChainConfig, gotDestChainConfig0); - _assertFeeQuoterDestChainConfigsEqual(destChainConfigArgs[1].destChainConfig, gotDestChainConfig1); - } - - function test_applyDestChainConfigUpdatesZeroInput_Success() public { - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](0); - - vm.recordLogs(); - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - assertEq(vm.getRecordedLogs().length, 0); - } - - // Reverts - - function test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero_Revert() public { - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; - - destChainConfigArg.destChainConfig.defaultTxGasLimit = 0; - vm.expectRevert( - abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) - ); - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - } - - function test_applyDestChainConfigUpdatesDefaultTxGasLimitGtMaxPerMessageGasLimit_Revert() public { - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; - - // Allow setting to the max value - destChainConfigArg.destChainConfig.defaultTxGasLimit = destChainConfigArg.destChainConfig.maxPerMsgGasLimit; - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - // Revert when exceeding max value - destChainConfigArg.destChainConfig.defaultTxGasLimit = destChainConfigArg.destChainConfig.maxPerMsgGasLimit + 1; - vm.expectRevert( - abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) - ); - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - } - - function test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() public { - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; - - destChainConfigArg.destChainSelector = 0; - vm.expectRevert( - abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) - ); - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - } - - function test_InvalidChainFamilySelector_Revert() public { - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - FeeQuoter.DestChainConfigArgs memory destChainConfigArg = destChainConfigArgs[0]; - - destChainConfigArg.destChainConfig.chainFamilySelector = bytes4(uint32(1)); - - vm.expectRevert( - abi.encodeWithSelector(FeeQuoter.InvalidDestChainConfig.selector, destChainConfigArg.destChainSelector) - ); - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - } -} - -contract FeeQuoter_getDataAvailabilityCost is FeeQuoterSetup { - function test_EmptyMessageCalculatesDataAvailabilityCost_Success() public { - uint256 dataAvailabilityCostUSD = - s_feeQuoter.getDataAvailabilityCost(DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, 0, 0, 0); - - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - - uint256 dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas - + destChainConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; - uint256 expectedDataAvailabilityCostUSD = - USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; - - assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); - - // Test that the cost is destination chain specific - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - destChainConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR + 1; - destChainConfigArgs[0].destChainConfig.destDataAvailabilityOverheadGas = - destChainConfig.destDataAvailabilityOverheadGas * 2; - destChainConfigArgs[0].destChainConfig.destGasPerDataAvailabilityByte = - destChainConfig.destGasPerDataAvailabilityByte * 2; - destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = - destChainConfig.destDataAvailabilityMultiplierBps * 2; - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR + 1); - uint256 dataAvailabilityCostUSD2 = - s_feeQuoter.getDataAvailabilityCost(DEST_CHAIN_SELECTOR + 1, USD_PER_DATA_AVAILABILITY_GAS, 0, 0, 0); - dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas - + destChainConfig.destGasPerDataAvailabilityByte * Internal.MESSAGE_FIXED_BYTES; - expectedDataAvailabilityCostUSD = - USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; - - assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD2); - assertFalse(dataAvailabilityCostUSD == dataAvailabilityCostUSD2); - } - - function test_SimpleMessageCalculatesDataAvailabilityCost_Success() public view { - uint256 dataAvailabilityCostUSD = - s_feeQuoter.getDataAvailabilityCost(DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, 100, 5, 50); - - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - - uint256 dataAvailabilityLengthBytes = - Internal.MESSAGE_FIXED_BYTES + 100 + (5 * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + 50; - uint256 dataAvailabilityGas = destChainConfig.destDataAvailabilityOverheadGas - + destChainConfig.destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; - uint256 expectedDataAvailabilityCostUSD = - USD_PER_DATA_AVAILABILITY_GAS * dataAvailabilityGas * destChainConfig.destDataAvailabilityMultiplierBps * 1e14; - - assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); - } - - function test_SimpleMessageCalculatesDataAvailabilityCostUnsupportedDestChainSelector_Success() public view { - uint256 dataAvailabilityCostUSD = s_feeQuoter.getDataAvailabilityCost(0, USD_PER_DATA_AVAILABILITY_GAS, 100, 5, 50); - - assertEq(dataAvailabilityCostUSD, 0); - } - - function test_Fuzz_ZeroDataAvailabilityGasPriceAlwaysCalculatesZeroDataAvailabilityCost_Success( - uint64 messageDataLength, - uint32 numberOfTokens, - uint32 tokenTransferBytesOverhead - ) public view { - uint256 dataAvailabilityCostUSD = s_feeQuoter.getDataAvailabilityCost( - DEST_CHAIN_SELECTOR, 0, messageDataLength, numberOfTokens, tokenTransferBytesOverhead - ); - - assertEq(0, dataAvailabilityCostUSD); - } - - function test_Fuzz_CalculateDataAvailabilityCost_Success( - uint64 destChainSelector, - uint32 destDataAvailabilityOverheadGas, - uint16 destGasPerDataAvailabilityByte, - uint16 destDataAvailabilityMultiplierBps, - uint112 dataAvailabilityGasPrice, - uint64 messageDataLength, - uint32 numberOfTokens, - uint32 tokenTransferBytesOverhead - ) public { - vm.assume(destChainSelector != 0); - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](1); - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(destChainSelector); - destChainConfigArgs[0] = - FeeQuoter.DestChainConfigArgs({destChainSelector: destChainSelector, destChainConfig: destChainConfig}); - destChainConfigArgs[0].destChainConfig.destDataAvailabilityOverheadGas = destDataAvailabilityOverheadGas; - destChainConfigArgs[0].destChainConfig.destGasPerDataAvailabilityByte = destGasPerDataAvailabilityByte; - destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = destDataAvailabilityMultiplierBps; - destChainConfigArgs[0].destChainConfig.defaultTxGasLimit = GAS_LIMIT; - destChainConfigArgs[0].destChainConfig.maxPerMsgGasLimit = GAS_LIMIT; - destChainConfigArgs[0].destChainConfig.chainFamilySelector = Internal.CHAIN_FAMILY_SELECTOR_EVM; - - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - uint256 dataAvailabilityCostUSD = s_feeQuoter.getDataAvailabilityCost( - destChainConfigArgs[0].destChainSelector, - dataAvailabilityGasPrice, - messageDataLength, - numberOfTokens, - tokenTransferBytesOverhead - ); - - uint256 dataAvailabilityLengthBytes = Internal.MESSAGE_FIXED_BYTES + messageDataLength - + (numberOfTokens * Internal.MESSAGE_FIXED_BYTES_PER_TOKEN) + tokenTransferBytesOverhead; - - uint256 dataAvailabilityGas = - destDataAvailabilityOverheadGas + destGasPerDataAvailabilityByte * dataAvailabilityLengthBytes; - uint256 expectedDataAvailabilityCostUSD = - dataAvailabilityGasPrice * dataAvailabilityGas * destDataAvailabilityMultiplierBps * 1e14; - - assertEq(expectedDataAvailabilityCostUSD, dataAvailabilityCostUSD); - } -} - -contract FeeQuoter_applyPremiumMultiplierWeiPerEthUpdates is FeeQuoterSetup { - function test_Fuzz_applyPremiumMultiplierWeiPerEthUpdates_Success( - FeeQuoter.PremiumMultiplierWeiPerEthArgs memory premiumMultiplierWeiPerEthArg - ) public { - FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs = - new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](1); - premiumMultiplierWeiPerEthArgs[0] = premiumMultiplierWeiPerEthArg; - - vm.expectEmit(); - emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( - premiumMultiplierWeiPerEthArg.token, premiumMultiplierWeiPerEthArg.premiumMultiplierWeiPerEth - ); - - s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); - - assertEq( - premiumMultiplierWeiPerEthArg.premiumMultiplierWeiPerEth, - s_feeQuoter.getPremiumMultiplierWeiPerEth(premiumMultiplierWeiPerEthArg.token) - ); - } - - function test_applyPremiumMultiplierWeiPerEthUpdatesSingleToken_Success() public { - FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs = - new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](1); - premiumMultiplierWeiPerEthArgs[0] = s_feeQuoterPremiumMultiplierWeiPerEthArgs[0]; - premiumMultiplierWeiPerEthArgs[0].token = vm.addr(1); - - vm.expectEmit(); - emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( - vm.addr(1), premiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth - ); - - s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); - - assertEq( - s_feeQuoterPremiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth, - s_feeQuoter.getPremiumMultiplierWeiPerEth(vm.addr(1)) - ); - } - - function test_applyPremiumMultiplierWeiPerEthUpdatesMultipleTokens_Success() public { - FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs = - new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](2); - premiumMultiplierWeiPerEthArgs[0] = s_feeQuoterPremiumMultiplierWeiPerEthArgs[0]; - premiumMultiplierWeiPerEthArgs[0].token = vm.addr(1); - premiumMultiplierWeiPerEthArgs[1].token = vm.addr(2); - - vm.expectEmit(); - emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( - vm.addr(1), premiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth - ); - vm.expectEmit(); - emit FeeQuoter.PremiumMultiplierWeiPerEthUpdated( - vm.addr(2), premiumMultiplierWeiPerEthArgs[1].premiumMultiplierWeiPerEth - ); - - s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); - - assertEq( - premiumMultiplierWeiPerEthArgs[0].premiumMultiplierWeiPerEth, - s_feeQuoter.getPremiumMultiplierWeiPerEth(vm.addr(1)) - ); - assertEq( - premiumMultiplierWeiPerEthArgs[1].premiumMultiplierWeiPerEth, - s_feeQuoter.getPremiumMultiplierWeiPerEth(vm.addr(2)) - ); - } - - function test_applyPremiumMultiplierWeiPerEthUpdatesZeroInput() public { - vm.recordLogs(); - s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](0)); - - assertEq(vm.getRecordedLogs().length, 0); - } - - // Reverts - - function test_OnlyCallableByOwnerOrAdmin_Revert() public { - FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory premiumMultiplierWeiPerEthArgs; - vm.startPrank(STRANGER); - - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - - s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs); - } -} - -contract FeeQuoter_applyTokenTransferFeeConfigUpdates is FeeQuoterSetup { - function test_Fuzz_ApplyTokenTransferFeeConfig_Success( - FeeQuoter.TokenTransferFeeConfig[2] memory tokenTransferFeeConfigs - ) public { - // To prevent Invalid Fee Range error from the fuzzer, bound the results to a valid range that - // where minFee < maxFee - tokenTransferFeeConfigs[0].minFeeUSDCents = - uint32(bound(tokenTransferFeeConfigs[0].minFeeUSDCents, 0, type(uint8).max)); - tokenTransferFeeConfigs[1].minFeeUSDCents = - uint32(bound(tokenTransferFeeConfigs[1].minFeeUSDCents, 0, type(uint8).max)); - - tokenTransferFeeConfigs[0].maxFeeUSDCents = uint32( - bound(tokenTransferFeeConfigs[0].maxFeeUSDCents, tokenTransferFeeConfigs[0].minFeeUSDCents + 1, type(uint32).max) - ); - tokenTransferFeeConfigs[1].maxFeeUSDCents = uint32( - bound(tokenTransferFeeConfigs[1].maxFeeUSDCents, tokenTransferFeeConfigs[1].minFeeUSDCents + 1, type(uint32).max) - ); - - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(2, 2); - tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - tokenTransferFeeConfigArgs[1].destChainSelector = DEST_CHAIN_SELECTOR + 1; - - for (uint256 i = 0; i < tokenTransferFeeConfigArgs.length; ++i) { - for (uint256 j = 0; j < tokenTransferFeeConfigs.length; ++j) { - tokenTransferFeeConfigs[j].destBytesOverhead = uint32( - bound(tokenTransferFeeConfigs[j].destBytesOverhead, Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, type(uint32).max) - ); - address feeToken = s_sourceTokens[j]; - tokenTransferFeeConfigArgs[i].tokenTransferFeeConfigs[j].token = feeToken; - tokenTransferFeeConfigArgs[i].tokenTransferFeeConfigs[j].tokenTransferFeeConfig = tokenTransferFeeConfigs[j]; - - vm.expectEmit(); - emit FeeQuoter.TokenTransferFeeConfigUpdated( - tokenTransferFeeConfigArgs[i].destChainSelector, feeToken, tokenTransferFeeConfigs[j] - ); - } - } - - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - - for (uint256 i = 0; i < tokenTransferFeeConfigs.length; ++i) { - _assertTokenTransferFeeConfigEqual( - tokenTransferFeeConfigs[i], - s_feeQuoter.getTokenTransferFeeConfig( - tokenTransferFeeConfigArgs[0].destChainSelector, - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[i].token - ) - ); - } - } - - function test_ApplyTokenTransferFeeConfig_Success() public { - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 2); - tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = address(5); - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ - minFeeUSDCents: 6, - maxFeeUSDCents: 7, - deciBps: 8, - destGasOverhead: 9, - destBytesOverhead: 312, - isEnabled: true - }); - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token = address(11); - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ - minFeeUSDCents: 12, - maxFeeUSDCents: 13, - deciBps: 14, - destGasOverhead: 15, - destBytesOverhead: 394, - isEnabled: true - }); - - vm.expectEmit(); - emit FeeQuoter.TokenTransferFeeConfigUpdated( - tokenTransferFeeConfigArgs[0].destChainSelector, - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token, - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig - ); - vm.expectEmit(); - emit FeeQuoter.TokenTransferFeeConfigUpdated( - tokenTransferFeeConfigArgs[0].destChainSelector, - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token, - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig - ); - - FeeQuoter.TokenTransferFeeConfigRemoveArgs[] memory tokensToRemove = - new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0); - s_feeQuoter.applyTokenTransferFeeConfigUpdates(tokenTransferFeeConfigArgs, tokensToRemove); - - FeeQuoter.TokenTransferFeeConfig memory config0 = s_feeQuoter.getTokenTransferFeeConfig( - tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token - ); - FeeQuoter.TokenTransferFeeConfig memory config1 = s_feeQuoter.getTokenTransferFeeConfig( - tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token - ); - - _assertTokenTransferFeeConfigEqual( - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig, config0 - ); - _assertTokenTransferFeeConfigEqual( - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig, config1 - ); - - // Remove only the first token and validate only the first token is removed - tokensToRemove = new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](1); - tokensToRemove[0] = FeeQuoter.TokenTransferFeeConfigRemoveArgs({ - destChainSelector: tokenTransferFeeConfigArgs[0].destChainSelector, - token: tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token - }); - - vm.expectEmit(); - emit FeeQuoter.TokenTransferFeeConfigDeleted( - tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token - ); - - s_feeQuoter.applyTokenTransferFeeConfigUpdates(new FeeQuoter.TokenTransferFeeConfigArgs[](0), tokensToRemove); - - config0 = s_feeQuoter.getTokenTransferFeeConfig( - tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token - ); - config1 = s_feeQuoter.getTokenTransferFeeConfig( - tokenTransferFeeConfigArgs[0].destChainSelector, tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].token - ); - - FeeQuoter.TokenTransferFeeConfig memory emptyConfig; - - _assertTokenTransferFeeConfigEqual(emptyConfig, config0); - _assertTokenTransferFeeConfigEqual( - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig, config1 - ); - } - - function test_ApplyTokenTransferFeeZeroInput() public { - vm.recordLogs(); - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - new FeeQuoter.TokenTransferFeeConfigArgs[](0), new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - - assertEq(vm.getRecordedLogs().length, 0); - } - - // Reverts - - function test_OnlyCallableByOwnerOrAdmin_Revert() public { - vm.startPrank(STRANGER); - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs; - - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - } - - function test_InvalidDestBytesOverhead_Revert() public { - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); - tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = address(5); - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ - minFeeUSDCents: 6, - maxFeeUSDCents: 7, - deciBps: 8, - destGasOverhead: 9, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES - 1), - isEnabled: true - }); - - vm.expectRevert( - abi.encodeWithSelector( - FeeQuoter.InvalidDestBytesOverhead.selector, - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token, - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.destBytesOverhead - ) - ); - - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - } -} - -contract FeeQuoter_getTokenTransferCost is FeeQuoterFeeSetup { - using USDPriceWith18Decimals for uint224; - - function test_NoTokenTransferChargesZeroFee_Success() public view { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(0, feeUSDWei); - assertEq(0, destGasOverhead); - assertEq(0, destBytesOverhead); - } - - function test_getTokenTransferCost_selfServeUsesDefaults_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_selfServeTokenDefaultPricing, 1000); - - // Get config to assert it isn't set - FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); - - assertFalse(transferFeeConfig.isEnabled); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - // Assert that the default values are used - assertEq(uint256(DEFAULT_TOKEN_FEE_USD_CENTS) * 1e16, feeUSDWei); - assertEq(DEFAULT_TOKEN_DEST_GAS_OVERHEAD, destGasOverhead); - assertEq(DEFAULT_TOKEN_BYTES_OVERHEAD, destBytesOverhead); - } - - function test_SmallTokenTransferChargesMinFeeAndGas_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1000); - FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(_configUSDCentToWei(transferFeeConfig.minFeeUSDCents), feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); - } - - function test_ZeroAmountTokenTransferChargesMinFeeAndGas_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 0); - FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(_configUSDCentToWei(transferFeeConfig.minFeeUSDCents), feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); - } - - function test_LargeTokenTransferChargesMaxFeeAndGas_Success() public view { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1e36); - FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - assertEq(_configUSDCentToWei(transferFeeConfig.maxFeeUSDCents), feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); - } - - function test_FeeTokenBpsFee_Success() public view { - uint256 tokenAmount = 10000e18; - - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); - FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - uint256 usdWei = _calcUSDValueFromTokenAmount(s_feeTokenPrice, tokenAmount); - uint256 bpsUSDWei = _applyBpsRatio( - usdWei, s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.deciBps - ); - - assertEq(bpsUSDWei, feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); - } - - function test_CustomTokenBpsFee_Success() public view { - uint256 tokenAmount = 200000e18; - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](1), - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - message.tokenAmounts[0] = Client.EVMTokenAmount({token: CUSTOM_TOKEN, amount: tokenAmount}); - - FeeQuoter.TokenTransferFeeConfig memory transferFeeConfig = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token); - - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - uint256 usdWei = _calcUSDValueFromTokenAmount(s_customTokenPrice, tokenAmount); - uint256 bpsUSDWei = _applyBpsRatio( - usdWei, s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[1].tokenTransferFeeConfig.deciBps - ); - - assertEq(bpsUSDWei, feeUSDWei); - assertEq(transferFeeConfig.destGasOverhead, destGasOverhead); - assertEq(transferFeeConfig.destBytesOverhead, destBytesOverhead); - } - - function test_ZeroFeeConfigChargesMinFee_Success() public { - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); - tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = s_sourceFeeToken; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ - minFeeUSDCents: 0, - maxFeeUSDCents: 1, - deciBps: 0, - destGasOverhead: 0, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES), - isEnabled: true - }); - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, 1e36); - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_feeTokenPrice, message.tokenAmounts); - - // if token charges 0 bps, it should cost minFee to transfer - assertEq( - _configUSDCentToWei( - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.minFeeUSDCents - ), - feeUSDWei - ); - assertEq(0, destGasOverhead); - assertEq(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, destBytesOverhead); - } - - function test_Fuzz_TokenTransferFeeDuplicateTokens_Success(uint256 transfers, uint256 amount) public view { - // It shouldn't be possible to pay materially lower fees by splitting up the transfers. - // Note it is possible to pay higher fees since the minimum fees are added. - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - transfers = bound(transfers, 1, destChainConfig.maxNumberOfTokensPerMsg); - // Cap amount to avoid overflow - amount = bound(amount, 0, 1e36); - Client.EVMTokenAmount[] memory multiple = new Client.EVMTokenAmount[](transfers); - for (uint256 i = 0; i < transfers; ++i) { - multiple[i] = Client.EVMTokenAmount({token: s_sourceTokens[0], amount: amount}); - } - Client.EVMTokenAmount[] memory single = new Client.EVMTokenAmount[](1); - single[0] = Client.EVMTokenAmount({token: s_sourceTokens[0], amount: amount * transfers}); - - address feeToken = s_sourceRouter.getWrappedNative(); - - (uint256 feeSingleUSDWei, uint32 gasOverheadSingle, uint32 bytesOverheadSingle) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, feeToken, s_wrappedTokenPrice, single); - (uint256 feeMultipleUSDWei, uint32 gasOverheadMultiple, uint32 bytesOverheadMultiple) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, feeToken, s_wrappedTokenPrice, multiple); - - // Note that there can be a rounding error once per split. - assertGe(feeMultipleUSDWei, (feeSingleUSDWei - destChainConfig.maxNumberOfTokensPerMsg)); - assertEq(gasOverheadMultiple, gasOverheadSingle * transfers); - assertEq(bytesOverheadMultiple, bytesOverheadSingle * transfers); - } - - function test_MixedTokenTransferFee_Success() public view { - address[3] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative(), CUSTOM_TOKEN]; - uint224[3] memory tokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice, s_customTokenPrice]; - FeeQuoter.TokenTransferFeeConfig[3] memory tokenTransferFeeConfigs = [ - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[0]), - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[1]), - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[2]) - ]; - - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](3), - feeToken: s_sourceRouter.getWrappedNative(), - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - uint256 expectedTotalGas = 0; - uint256 expectedTotalBytes = 0; - - // Start with small token transfers, total bps fee is lower than min token transfer fee - for (uint256 i = 0; i < testTokens.length; ++i) { - message.tokenAmounts[i] = Client.EVMTokenAmount({token: testTokens[i], amount: 1e14}); - FeeQuoter.TokenTransferFeeConfig memory tokenTransferFeeConfig = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, testTokens[i]); - - expectedTotalGas += tokenTransferFeeConfig.destGasOverhead == 0 - ? DEFAULT_TOKEN_DEST_GAS_OVERHEAD - : tokenTransferFeeConfig.destGasOverhead; - expectedTotalBytes += tokenTransferFeeConfig.destBytesOverhead == 0 - ? DEFAULT_TOKEN_BYTES_OVERHEAD - : tokenTransferFeeConfig.destBytesOverhead; - } - (uint256 feeUSDWei, uint32 destGasOverhead, uint32 destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); - - uint256 expectedFeeUSDWei = 0; - for (uint256 i = 0; i < testTokens.length; ++i) { - expectedFeeUSDWei += _configUSDCentToWei( - tokenTransferFeeConfigs[i].minFeeUSDCents == 0 - ? DEFAULT_TOKEN_FEE_USD_CENTS - : tokenTransferFeeConfigs[i].minFeeUSDCents - ); - } - - assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 1"); - assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 1"); - assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 1"); - - // Set 1st token transfer to a meaningful amount so its bps fee is now between min and max fee - message.tokenAmounts[0] = Client.EVMTokenAmount({token: testTokens[0], amount: 10000e18}); - - uint256 token0USDWei = _applyBpsRatio( - _calcUSDValueFromTokenAmount(tokenPrices[0], message.tokenAmounts[0].amount), tokenTransferFeeConfigs[0].deciBps - ); - uint256 token1USDWei = _configUSDCentToWei(DEFAULT_TOKEN_FEE_USD_CENTS); - - (feeUSDWei, destGasOverhead, destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); - expectedFeeUSDWei = token0USDWei + token1USDWei + _configUSDCentToWei(tokenTransferFeeConfigs[2].minFeeUSDCents); - - assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 2"); - assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 2"); - assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 2"); - - // Set 2nd token transfer to a large amount that is higher than maxFeeUSD - message.tokenAmounts[2] = Client.EVMTokenAmount({token: testTokens[2], amount: 1e36}); - - (feeUSDWei, destGasOverhead, destBytesOverhead) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, s_wrappedTokenPrice, message.tokenAmounts); - expectedFeeUSDWei = token0USDWei + token1USDWei + _configUSDCentToWei(tokenTransferFeeConfigs[2].maxFeeUSDCents); - - assertEq(expectedFeeUSDWei, feeUSDWei, "wrong feeUSDWei 3"); - assertEq(expectedTotalGas, destGasOverhead, "wrong destGasOverhead 3"); - assertEq(expectedTotalBytes, destBytesOverhead, "wrong destBytesOverhead 3"); - } -} - -contract FeeQuoter_getValidatedFee is FeeQuoterFeeSetup { - using USDPriceWith18Decimals for uint224; - - function test_EmptyMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = testTokens[i]; - uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - - uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD; - uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - uint256 messageFeeUSD = (_configUSDCentToWei(destChainConfig.networkFeeUSDCents) * premiumMultiplierWeiPerEth); - uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( - DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, 0 - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, feeAmount); - } - } - - function test_ZeroDataAvailabilityMultiplier_Success() public { - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = new FeeQuoter.DestChainConfigArgs[](1); - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - destChainConfigArgs[0] = - FeeQuoter.DestChainConfigArgs({destChainSelector: DEST_CHAIN_SELECTOR, destChainConfig: destChainConfig}); - destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = 0; - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); - - uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD; - uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - uint256 messageFeeUSD = (_configUSDCentToWei(destChainConfig.networkFeeUSDCents) * premiumMultiplierWeiPerEth); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD) / s_feeTokenPrice; - assertEq(totalPriceInFeeToken, feeAmount); - } - - function test_HighGasMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - uint256 customGasLimit = MAX_GAS_LIMIT; - uint256 customDataSize = MAX_DATA_SIZE; - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: new bytes(customDataSize), - tokenAmounts: new Client.EVMTokenAmount[](0), - feeToken: testTokens[i], - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: customGasLimit})) - }); - - uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - - uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - uint256 gasUsed = customGasLimit + DEST_GAS_OVERHEAD + customDataSize * DEST_GAS_PER_PAYLOAD_BYTE; - uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - uint256 messageFeeUSD = (_configUSDCentToWei(destChainConfig.networkFeeUSDCents) * premiumMultiplierWeiPerEth); - uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( - DEST_CHAIN_SELECTOR, USD_PER_DATA_AVAILABILITY_GAS, message.data.length, message.tokenAmounts.length, 0 - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, feeAmount); - } - } - - function test_SingleTokenMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - uint256 tokenAmount = 10000e18; - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); - message.feeToken = testTokens[i]; - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - uint32 destBytesOverhead = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token).destBytesOverhead; - uint32 tokenBytesOverhead = - destBytesOverhead == 0 ? uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) : destBytesOverhead; - - uint256 feeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - - uint256 gasUsed = GAS_LIMIT + DEST_GAS_OVERHEAD - + s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[0].token).destGasOverhead; - uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - (uint256 transferFeeUSD,,) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, feeTokenPrices[i], message.tokenAmounts); - uint256 messageFeeUSD = (transferFeeUSD * s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken)); - uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( - DEST_CHAIN_SELECTOR, - USD_PER_DATA_AVAILABILITY_GAS, - message.data.length, - message.tokenAmounts.length, - tokenBytesOverhead - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, feeAmount); - } - } - - function test_MessageWithDataAndTokenTransfer_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - uint256 customGasLimit = 1_000_000; - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: new Client.EVMTokenAmount[](2), - feeToken: testTokens[i], - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: customGasLimit})) - }); - uint64 premiumMultiplierWeiPerEth = s_feeQuoter.getPremiumMultiplierWeiPerEth(message.feeToken); - FeeQuoter.DestChainConfig memory destChainConfig = s_feeQuoter.getDestChainConfig(DEST_CHAIN_SELECTOR); - - message.tokenAmounts[0] = Client.EVMTokenAmount({token: s_sourceFeeToken, amount: 10000e18}); // feeTokenAmount - message.tokenAmounts[1] = Client.EVMTokenAmount({token: CUSTOM_TOKEN, amount: 200000e18}); // customTokenAmount - message.data = "random bits and bytes that should be factored into the cost of the message"; - - uint32 tokenGasOverhead = 0; - uint32 tokenBytesOverhead = 0; - for (uint256 j = 0; j < message.tokenAmounts.length; ++j) { - tokenGasOverhead += - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[j].token).destGasOverhead; - uint32 destBytesOverhead = - s_feeQuoter.getTokenTransferFeeConfig(DEST_CHAIN_SELECTOR, message.tokenAmounts[j].token).destBytesOverhead; - tokenBytesOverhead += destBytesOverhead == 0 ? uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) : destBytesOverhead; - } - - uint256 gasUsed = - customGasLimit + DEST_GAS_OVERHEAD + message.data.length * DEST_GAS_PER_PAYLOAD_BYTE + tokenGasOverhead; - uint256 gasFeeUSD = (gasUsed * destChainConfig.gasMultiplierWeiPerEth * USD_PER_GAS); - (uint256 transferFeeUSD,,) = - s_feeQuoter.getTokenTransferCost(DEST_CHAIN_SELECTOR, message.feeToken, feeTokenPrices[i], message.tokenAmounts); - uint256 messageFeeUSD = (transferFeeUSD * premiumMultiplierWeiPerEth); - uint256 dataAvailabilityFeeUSD = s_feeQuoter.getDataAvailabilityCost( - DEST_CHAIN_SELECTOR, - USD_PER_DATA_AVAILABILITY_GAS, - message.data.length, - message.tokenAmounts.length, - tokenBytesOverhead - ); - - uint256 totalPriceInFeeToken = (gasFeeUSD + messageFeeUSD + dataAvailabilityFeeUSD) / feeTokenPrices[i]; - assertEq(totalPriceInFeeToken, s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message)); - } - } - - function test_Fuzz_EnforceOutOfOrder(bool enforce, bool allowOutOfOrderExecution) public { - // Update config to enforce allowOutOfOrderExecution = defaultVal. - vm.stopPrank(); - vm.startPrank(OWNER); - - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - destChainConfigArgs[0].destChainConfig.enforceOutOfOrder = enforce; - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, - Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: allowOutOfOrderExecution}) - ); - - // If enforcement is on, only true should be allowed. - if (enforce && !allowOutOfOrderExecution) { - vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); - } - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - } - - // Reverts - - function test_DestinationChainNotEnabled_Revert() public { - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.DestinationChainNotEnabled.selector, DEST_CHAIN_SELECTOR + 1)); - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR + 1, _generateEmptyMessage()); - } - - function test_EnforceOutOfOrder_Revert() public { - // Update config to enforce allowOutOfOrderExecution = true. - vm.stopPrank(); - vm.startPrank(OWNER); - - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - destChainConfigArgs[0].destChainConfig.enforceOutOfOrder = true; - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - vm.stopPrank(); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - // Empty extraArgs to should revert since it enforceOutOfOrder is true. - message.extraArgs = ""; - - vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - } - - function test_MessageTooLarge_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.data = new bytes(MAX_DATA_SIZE + 1); - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.MessageTooLarge.selector, MAX_DATA_SIZE, message.data.length)); - - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - } - - function test_TooManyTokens_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - uint256 tooMany = MAX_TOKENS_LENGTH + 1; - message.tokenAmounts = new Client.EVMTokenAmount[](tooMany); - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.UnsupportedNumberOfTokens.selector, tooMany, MAX_TOKENS_LENGTH)); - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - } - - // Asserts gasLimit must be <=maxGasLimit - function test_MessageGasLimitTooHigh_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: MAX_GAS_LIMIT + 1})); - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.MessageGasLimitTooHigh.selector)); - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - } - - function test_NotAFeeToken_Revert() public { - address notAFeeToken = address(0x111111); - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(notAFeeToken, 1); - message.feeToken = notAFeeToken; - - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.FeeTokenNotSupported.selector, notAFeeToken)); - - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - } - - function test_InvalidEVMAddress_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.receiver = abi.encode(type(uint208).max); - - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, message.receiver)); - - s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - } -} - -contract FeeQuoter_processMessageArgs is FeeQuoterFeeSetup { - using USDPriceWith18Decimals for uint224; - - function setUp() public virtual override { - super.setUp(); - } - - function test_processMessageArgs_WithLinkTokenAmount_Success() public view { - ( - uint256 msgFeeJuels, - /* bool isOutOfOrderExecution */ - , - /* bytes memory convertedExtraArgs */ - , - /* destExecDataPerToken */ - ) = s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - // LINK - s_sourceTokens[0], - MAX_MSG_FEES_JUELS, - "", - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - - assertEq(msgFeeJuels, MAX_MSG_FEES_JUELS); - } - - function test_processMessageArgs_WithConvertedTokenAmount_Success() public view { - address feeToken = s_sourceTokens[1]; - uint256 feeTokenAmount = 10_000 gwei; - uint256 expectedConvertedAmount = s_feeQuoter.convertTokenAmount(feeToken, feeTokenAmount, s_sourceTokens[0]); - - ( - uint256 msgFeeJuels, - /* bool isOutOfOrderExecution */ - , - /* bytes memory convertedExtraArgs */ - , - /* destExecDataPerToken */ - ) = s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - feeToken, - feeTokenAmount, - "", - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - - assertEq(msgFeeJuels, expectedConvertedAmount); - } - - function test_processMessageArgs_WithEmptyEVMExtraArgs_Success() public view { - ( - /* uint256 msgFeeJuels */ - , - bool isOutOfOrderExecution, - bytes memory convertedExtraArgs, - /* destExecDataPerToken */ - ) = s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - s_sourceTokens[0], - 0, - "", - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - - assertEq(isOutOfOrderExecution, false); - assertEq(convertedExtraArgs, Client._argsToBytes(s_feeQuoter.parseEVMExtraArgsFromBytes("", DEST_CHAIN_SELECTOR))); - } - - function test_processMessageArgs_WithEVMExtraArgsV1_Success() public view { - bytes memory extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 1000})); - - ( - /* uint256 msgFeeJuels */ - , - bool isOutOfOrderExecution, - bytes memory convertedExtraArgs, - /* destExecDataPerToken */ - ) = s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - s_sourceTokens[0], - 0, - extraArgs, - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - - assertEq(isOutOfOrderExecution, false); - assertEq( - convertedExtraArgs, Client._argsToBytes(s_feeQuoter.parseEVMExtraArgsFromBytes(extraArgs, DEST_CHAIN_SELECTOR)) - ); - } - - function test_processMessageArgs_WitEVMExtraArgsV2_Success() public view { - bytes memory extraArgs = Client._argsToBytes(Client.EVMExtraArgsV2({gasLimit: 0, allowOutOfOrderExecution: true})); - - ( - /* uint256 msgFeeJuels */ - , - bool isOutOfOrderExecution, - bytes memory convertedExtraArgs, - /* destExecDataPerToken */ - ) = s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - s_sourceTokens[0], - 0, - extraArgs, - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - - assertEq(isOutOfOrderExecution, true); - assertEq( - convertedExtraArgs, Client._argsToBytes(s_feeQuoter.parseEVMExtraArgsFromBytes(extraArgs, DEST_CHAIN_SELECTOR)) - ); - } - - // Reverts - - function test_processMessageArgs_MessageFeeTooHigh_Revert() public { - vm.expectRevert( - abi.encodeWithSelector(FeeQuoter.MessageFeeTooHigh.selector, MAX_MSG_FEES_JUELS + 1, MAX_MSG_FEES_JUELS) - ); - - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - s_sourceTokens[0], - MAX_MSG_FEES_JUELS + 1, - "", - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - } - - function test_processMessageArgs_InvalidExtraArgs_Revert() public { - vm.expectRevert(FeeQuoter.InvalidExtraArgsTag.selector); - - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - s_sourceTokens[0], - 0, - "wrong extra args", - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - } - - function test_processMessageArgs_MalformedEVMExtraArgs_Revert() public { - // abi.decode error - vm.expectRevert(); - - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - s_sourceTokens[0], - 0, - abi.encodeWithSelector(Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV1({gasLimit: 100})), - new Internal.EVM2AnyTokenTransfer[](0), - new Client.EVMTokenAmount[](0) - ); - } - - function test_processMessageArgs_WithCorrectPoolReturnData_Success() public view { - Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](2); - sourceTokenAmounts[0].amount = 1e18; - sourceTokenAmounts[0].token = s_sourceTokens[0]; - sourceTokenAmounts[1].amount = 1e18; - sourceTokenAmounts[1].token = CUSTOM_TOKEN_2; - - Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](2); - tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); - tokenAmounts[1] = _getSourceTokenData(sourceTokenAmounts[1], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); - bytes[] memory expectedDestExecData = new bytes[](2); - expectedDestExecData[0] = abi.encode( - s_feeQuoterTokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig.destGasOverhead - ); - expectedDestExecData[1] = abi.encode(DEFAULT_TOKEN_DEST_GAS_OVERHEAD); //expected return data should be abi.encoded default as isEnabled is false - - // No revert - successful - ( /* msgFeeJuels */ , /* isOutOfOrderExecution */, /* convertedExtraArgs */, bytes[] memory destExecData) = - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts - ); - - for (uint256 i = 0; i < destExecData.length; ++i) { - assertEq(destExecData[i], expectedDestExecData[i]); - } - } - - function test_processMessageArgs_TokenAmountArraysMismatching_Revert() public { - Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](2); - sourceTokenAmounts[0].amount = 1e18; - sourceTokenAmounts[0].token = s_sourceTokens[0]; - - Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](1); - tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); - - // Revert due to index out of bounds access - vm.expectRevert(); - - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, - s_sourceTokens[0], - MAX_MSG_FEES_JUELS, - "", - new Internal.EVM2AnyTokenTransfer[](1), - new Client.EVMTokenAmount[](0) - ); - } - - function test_applyTokensTransferFeeConfigUpdates_InvalidFeeRange_Revert() public { - address sourceETH = s_sourceTokens[1]; - - // Set token config to allow larger data - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); - tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = sourceETH; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ - minFeeUSDCents: 1, - maxFeeUSDCents: 0, - deciBps: 0, - destGasOverhead: 0, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, - isEnabled: true - }); - - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.InvalidFeeRange.selector, 1, 0)); - - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - } - - function test_processMessageArgs_SourceTokenDataTooLarge_Revert() public { - address sourceETH = s_sourceTokens[1]; - - Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](1); - sourceTokenAmounts[0].amount = 1000; - sourceTokenAmounts[0].token = sourceETH; - - Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](1); - tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); - - // No data set, should succeed - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts - ); - - // Set max data length, should succeed - tokenAmounts[0].extraData = new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES); - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts - ); - - // Set data to max length +1, should revert - tokenAmounts[0].extraData = new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + 1); - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts - ); - - // Set token config to allow larger data - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); - tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = sourceETH; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ - minFeeUSDCents: 0, - maxFeeUSDCents: 1, - deciBps: 0, - destGasOverhead: 0, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, - isEnabled: true - }); - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts - ); - - // Set the token data larger than the configured token data, should revert - tokenAmounts[0].extraData = new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + 32 + 1); - - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts - ); - } - - function test_processMessageArgs_InvalidEVMAddressDestToken_Revert() public { - bytes memory nonEvmAddress = abi.encode(type(uint208).max); - - Client.EVMTokenAmount[] memory sourceTokenAmounts = new Client.EVMTokenAmount[](1); - sourceTokenAmounts[0].amount = 1e18; - sourceTokenAmounts[0].token = s_sourceTokens[0]; - - Internal.EVM2AnyTokenTransfer[] memory tokenAmounts = new Internal.EVM2AnyTokenTransfer[](1); - tokenAmounts[0] = _getSourceTokenData(sourceTokenAmounts[0], s_tokenAdminRegistry, DEST_CHAIN_SELECTOR); - tokenAmounts[0].destTokenAddress = nonEvmAddress; - - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, nonEvmAddress)); - s_feeQuoter.processMessageArgs( - DEST_CHAIN_SELECTOR, s_sourceTokens[0], MAX_MSG_FEES_JUELS, "", tokenAmounts, sourceTokenAmounts - ); - } -} - -contract FeeQuoter_validateDestFamilyAddress is FeeQuoterSetup { - function test_ValidEVMAddress_Success() public view { - bytes memory encodedAddress = abi.encode(address(10000)); - s_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, encodedAddress); - } - - function test_ValidNonEVMAddress_Success() public view { - s_feeQuoter.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_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, invalidAddress); - } - - function test_InvalidEVMAddressEncodePacked_Revert() public { - bytes memory invalidAddress = abi.encodePacked(address(234)); - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, invalidAddress)); - s_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, invalidAddress); - } - - function test_InvalidEVMAddressPrecompiles_Revert() public { - for (uint160 i = 0; i < Internal.PRECOMPILE_SPACE; ++i) { - bytes memory invalidAddress = abi.encode(address(i)); - vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, invalidAddress)); - s_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, invalidAddress); - } - - s_feeQuoter.validateDestFamilyAddress( - Internal.CHAIN_FAMILY_SELECTOR_EVM, abi.encode(address(uint160(Internal.PRECOMPILE_SPACE))) - ); - } -} - -contract FeeQuoter_parseEVMExtraArgsFromBytes is FeeQuoterSetup { - FeeQuoter.DestChainConfig private s_destChainConfig; - - function setUp() public virtual override { - super.setUp(); - s_destChainConfig = _generateFeeQuoterDestChainConfigArgs()[0].destChainConfig; - } - - 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( - abi.encode(s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig)), - 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( - abi.encode(s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig)), abi.encode(inputArgs) - ); - } - - function test_EVMExtraArgsDefault_Success() public view { - Client.EVMExtraArgsV2 memory expectedOutputArgs = - Client.EVMExtraArgsV2({gasLimit: s_destChainConfig.defaultTxGasLimit, allowOutOfOrderExecution: false}); - - vm.assertEq( - abi.encode(s_feeQuoter.parseEVMExtraArgsFromBytes("", s_destChainConfig)), abi.encode(expectedOutputArgs) - ); - } - - // 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(FeeQuoter.InvalidExtraArgsTag.selector); - s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig); - } - - function test_EVMExtraArgsEnforceOutOfOrder_Revert() public { - Client.EVMExtraArgsV2 memory inputArgs = - Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT, allowOutOfOrderExecution: false}); - bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); - s_destChainConfig.enforceOutOfOrder = true; - - vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); - s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig); - } - - function test_EVMExtraArgsGasLimitTooHigh_Revert() public { - Client.EVMExtraArgsV2 memory inputArgs = - Client.EVMExtraArgsV2({gasLimit: s_destChainConfig.maxPerMsgGasLimit + 1, allowOutOfOrderExecution: true}); - bytes memory inputExtraArgs = Client._argsToBytes(inputArgs); - - vm.expectRevert(FeeQuoter.MessageGasLimitTooHigh.selector); - s_feeQuoter.parseEVMExtraArgsFromBytes(inputExtraArgs, s_destChainConfig); - } -} - -contract FeeQuoter_KeystoneSetup is FeeQuoterSetup { - address internal constant FORWARDER_1 = address(0x1); - address internal constant WORKFLOW_OWNER_1 = address(0x3); - bytes10 internal constant WORKFLOW_NAME_1 = "workflow1"; - bytes2 internal constant REPORT_NAME_1 = "01"; - address internal onReportTestToken1; - address internal onReportTestToken2; - - function setUp() public virtual override { - super.setUp(); - onReportTestToken1 = s_sourceTokens[0]; - onReportTestToken2 = _deploySourceToken("onReportTestToken2", 0, 20); - - KeystoneFeedsPermissionHandler.Permission[] memory permissions = new KeystoneFeedsPermissionHandler.Permission[](1); - permissions[0] = KeystoneFeedsPermissionHandler.Permission({ - forwarder: FORWARDER_1, - workflowOwner: WORKFLOW_OWNER_1, - workflowName: WORKFLOW_NAME_1, - reportName: REPORT_NAME_1, - isAllowed: true - }); - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeeds = new FeeQuoter.TokenPriceFeedUpdate[](2); - tokenPriceFeeds[0] = FeeQuoter.TokenPriceFeedUpdate({ - sourceToken: onReportTestToken1, - feedConfig: FeeQuoter.TokenPriceFeedConfig({dataFeedAddress: address(0x0), tokenDecimals: 18, isEnabled: true}) - }); - tokenPriceFeeds[1] = FeeQuoter.TokenPriceFeedUpdate({ - sourceToken: onReportTestToken2, - feedConfig: FeeQuoter.TokenPriceFeedConfig({dataFeedAddress: address(0x0), tokenDecimals: 20, isEnabled: true}) - }); - s_feeQuoter.setReportPermissions(permissions); - s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeeds); - } -} - -contract FeeQuoter_onReport is FeeQuoter_KeystoneSetup { - function test_onReport_Success() public { - bytes memory encodedPermissionsMetadata = - abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); - - FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](2); - report[0] = - FeeQuoter.ReceivedCCIPFeedReport({token: onReportTestToken1, price: 4e18, timestamp: uint32(block.timestamp)}); - report[1] = - FeeQuoter.ReceivedCCIPFeedReport({token: onReportTestToken2, price: 4e18, timestamp: uint32(block.timestamp)}); - - uint224 expectedStoredToken1Price = s_feeQuoter.calculateRebasedValue(18, 18, report[0].price); - uint224 expectedStoredToken2Price = s_feeQuoter.calculateRebasedValue(18, 20, report[1].price); - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(onReportTestToken1, expectedStoredToken1Price, block.timestamp); - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(onReportTestToken2, expectedStoredToken2Price, block.timestamp); - - changePrank(FORWARDER_1); - s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); - - vm.assertEq(s_feeQuoter.getTokenPrice(report[0].token).value, expectedStoredToken1Price); - vm.assertEq(s_feeQuoter.getTokenPrice(report[0].token).timestamp, report[0].timestamp); - - vm.assertEq(s_feeQuoter.getTokenPrice(report[1].token).value, expectedStoredToken2Price); - vm.assertEq(s_feeQuoter.getTokenPrice(report[1].token).timestamp, report[1].timestamp); - } - - function test_OnReport_StaleUpdate_SkipPriceUpdate_Success() public { - //Creating a correct report - bytes memory encodedPermissionsMetadata = - abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); - - FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); - report[0] = - FeeQuoter.ReceivedCCIPFeedReport({token: onReportTestToken1, price: 4e18, timestamp: uint32(block.timestamp)}); - - uint224 expectedStoredTokenPrice = s_feeQuoter.calculateRebasedValue(18, 18, report[0].price); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(onReportTestToken1, expectedStoredTokenPrice, block.timestamp); - - changePrank(FORWARDER_1); - //setting the correct price and time with the correct report - s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); - - //create a stale report - report[0] = - FeeQuoter.ReceivedCCIPFeedReport({token: onReportTestToken1, price: 4e18, timestamp: uint32(block.timestamp - 1)}); - - //record logs to check no events were emitted - vm.recordLogs(); - - s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); - - //no logs should have been emitted - assertEq(vm.getRecordedLogs().length, 0); - } - - function test_onReport_TokenNotSupported_Revert() public { - bytes memory encodedPermissionsMetadata = - abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); - FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); - report[0] = - FeeQuoter.ReceivedCCIPFeedReport({token: s_sourceTokens[1], price: 4e18, timestamp: uint32(block.timestamp)}); - - // Revert due to token config not being set with the isEnabled flag - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, s_sourceTokens[1])); - vm.startPrank(FORWARDER_1); - s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); - } - - function test_onReport_InvalidForwarder_Reverts() public { - bytes memory encodedPermissionsMetadata = - abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); - FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); - report[0] = - FeeQuoter.ReceivedCCIPFeedReport({token: s_sourceTokens[0], price: 4e18, timestamp: uint32(block.timestamp)}); - - vm.expectRevert( - abi.encodeWithSelector( - KeystoneFeedsPermissionHandler.ReportForwarderUnauthorized.selector, - STRANGER, - WORKFLOW_OWNER_1, - WORKFLOW_NAME_1, - REPORT_NAME_1 - ) - ); - changePrank(STRANGER); - s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); - } - - function test_onReport_UnsupportedToken_Reverts() public { - bytes memory encodedPermissionsMetadata = - abi.encodePacked(keccak256(abi.encode("workflowCID")), WORKFLOW_NAME_1, WORKFLOW_OWNER_1, REPORT_NAME_1); - FeeQuoter.ReceivedCCIPFeedReport[] memory report = new FeeQuoter.ReceivedCCIPFeedReport[](1); - report[0] = - FeeQuoter.ReceivedCCIPFeedReport({token: s_sourceTokens[1], price: 4e18, timestamp: uint32(block.timestamp)}); - - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.TokenNotSupported.selector, s_sourceTokens[1])); - changePrank(FORWARDER_1); - s_feeQuoter.onReport(encodedPermissionsMetadata, abi.encode(report)); - } -} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.updatePrices.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.updatePrices.t.sol new file mode 100644 index 00000000000..d40ac7d33ad --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.updatePrices.t.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_updatePrices is FeeQuoterSetup { + function test_OnlyTokenPrice_Success() public { + Internal.PriceUpdates memory update = Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), + gasPriceUpdates: new Internal.GasPriceUpdate[](0) + }); + update.tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: 4e18}); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated( + update.tokenPriceUpdates[0].sourceToken, update.tokenPriceUpdates[0].usdPerToken, block.timestamp + ); + + s_feeQuoter.updatePrices(update); + + assertEq(s_feeQuoter.getTokenPrice(s_sourceTokens[0]).value, update.tokenPriceUpdates[0].usdPerToken); + } + + function test_OnlyGasPrice_Success() public { + Internal.PriceUpdates memory update = Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), + gasPriceUpdates: new Internal.GasPriceUpdate[](1) + }); + update.gasPriceUpdates[0] = + Internal.GasPriceUpdate({destChainSelector: DEST_CHAIN_SELECTOR, usdPerUnitGas: 2000e18}); + + vm.expectEmit(); + emit FeeQuoter.UsdPerUnitGasUpdated( + update.gasPriceUpdates[0].destChainSelector, update.gasPriceUpdates[0].usdPerUnitGas, block.timestamp + ); + + s_feeQuoter.updatePrices(update); + + assertEq( + s_feeQuoter.getDestinationChainGasPrice(DEST_CHAIN_SELECTOR).value, update.gasPriceUpdates[0].usdPerUnitGas + ); + } + + function test_UpdateMultiplePrices_Success() public { + Internal.TokenPriceUpdate[] memory tokenPriceUpdates = new Internal.TokenPriceUpdate[](3); + tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: 4e18}); + tokenPriceUpdates[1] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[1], usdPerToken: 1800e18}); + tokenPriceUpdates[2] = Internal.TokenPriceUpdate({sourceToken: address(12345), usdPerToken: 1e18}); + + Internal.GasPriceUpdate[] memory gasPriceUpdates = new Internal.GasPriceUpdate[](3); + gasPriceUpdates[0] = Internal.GasPriceUpdate({destChainSelector: DEST_CHAIN_SELECTOR, usdPerUnitGas: 2e6}); + gasPriceUpdates[1] = Internal.GasPriceUpdate({destChainSelector: SOURCE_CHAIN_SELECTOR, usdPerUnitGas: 2000e18}); + gasPriceUpdates[2] = Internal.GasPriceUpdate({destChainSelector: 12345, usdPerUnitGas: 1e18}); + + Internal.PriceUpdates memory update = + Internal.PriceUpdates({tokenPriceUpdates: tokenPriceUpdates, gasPriceUpdates: gasPriceUpdates}); + + for (uint256 i = 0; i < tokenPriceUpdates.length; ++i) { + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated( + update.tokenPriceUpdates[i].sourceToken, update.tokenPriceUpdates[i].usdPerToken, block.timestamp + ); + } + for (uint256 i = 0; i < gasPriceUpdates.length; ++i) { + vm.expectEmit(); + emit FeeQuoter.UsdPerUnitGasUpdated( + update.gasPriceUpdates[i].destChainSelector, update.gasPriceUpdates[i].usdPerUnitGas, block.timestamp + ); + } + + s_feeQuoter.updatePrices(update); + + for (uint256 i = 0; i < tokenPriceUpdates.length; ++i) { + assertEq( + s_feeQuoter.getTokenPrice(update.tokenPriceUpdates[i].sourceToken).value, tokenPriceUpdates[i].usdPerToken + ); + } + for (uint256 i = 0; i < gasPriceUpdates.length; ++i) { + assertEq( + s_feeQuoter.getDestinationChainGasPrice(update.gasPriceUpdates[i].destChainSelector).value, + gasPriceUpdates[i].usdPerUnitGas + ); + } + } + + function test_UpdatableByAuthorizedCaller_Success() public { + Internal.PriceUpdates memory priceUpdates = Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](1), + gasPriceUpdates: new Internal.GasPriceUpdate[](0) + }); + priceUpdates.tokenPriceUpdates[0] = Internal.TokenPriceUpdate({sourceToken: s_sourceTokens[0], usdPerToken: 4e18}); + + // Revert when caller is not authorized + vm.startPrank(STRANGER); + vm.expectRevert(abi.encodeWithSelector(AuthorizedCallers.UnauthorizedCaller.selector, STRANGER)); + s_feeQuoter.updatePrices(priceUpdates); + + address[] memory priceUpdaters = new address[](1); + priceUpdaters[0] = STRANGER; + vm.startPrank(OWNER); + s_feeQuoter.applyAuthorizedCallerUpdates( + AuthorizedCallers.AuthorizedCallerArgs({addedCallers: priceUpdaters, removedCallers: new address[](0)}) + ); + + // Stranger is now an authorized caller to update prices + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated( + priceUpdates.tokenPriceUpdates[0].sourceToken, priceUpdates.tokenPriceUpdates[0].usdPerToken, block.timestamp + ); + s_feeQuoter.updatePrices(priceUpdates); + + assertEq(s_feeQuoter.getTokenPrice(s_sourceTokens[0]).value, priceUpdates.tokenPriceUpdates[0].usdPerToken); + + vm.startPrank(OWNER); + s_feeQuoter.applyAuthorizedCallerUpdates( + AuthorizedCallers.AuthorizedCallerArgs({addedCallers: new address[](0), removedCallers: priceUpdaters}) + ); + + // Revert when authorized caller is removed + vm.startPrank(STRANGER); + vm.expectRevert(abi.encodeWithSelector(AuthorizedCallers.UnauthorizedCaller.selector, STRANGER)); + s_feeQuoter.updatePrices(priceUpdates); + } + + // Reverts + + function test_OnlyCallableByUpdater_Revert() public { + Internal.PriceUpdates memory priceUpdates = Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), + gasPriceUpdates: new Internal.GasPriceUpdate[](0) + }); + + vm.startPrank(STRANGER); + vm.expectRevert(abi.encodeWithSelector(AuthorizedCallers.UnauthorizedCaller.selector, STRANGER)); + s_feeQuoter.updatePrices(priceUpdates); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.updateTokenPriceFeeds.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.updateTokenPriceFeeds.t.sol new file mode 100644 index 00000000000..9341fab121b --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.updateTokenPriceFeeds.t.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; +import {FeeQuoter} from "../../FeeQuoter.sol"; +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +import {Vm} from "forge-std/Vm.sol"; + +contract FeeQuoter_updateTokenPriceFeeds is FeeQuoterSetup { + function test_ZeroFeeds_Success() public { + Vm.Log[] memory logEntries = vm.getRecordedLogs(); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](0); + vm.recordLogs(); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + // Verify no log emissions + assertEq(logEntries.length, 0); + } + + function test_SingleFeedUpdate_Success() public { + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = + _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); + + _assertTokenPriceFeedConfigNotConfigured(s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken)); + + vm.expectEmit(); + emit FeeQuoter.PriceFeedPerTokenUpdated(tokenPriceFeedUpdates[0].sourceToken, tokenPriceFeedUpdates[0].feedConfig); + + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + _assertTokenPriceFeedConfigEquality( + s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig + ); + } + + function test_MultipleFeedUpdate_Success() public { + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](2); + + for (uint256 i = 0; i < 2; ++i) { + tokenPriceFeedUpdates[i] = + _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[i], s_dataFeedByToken[s_sourceTokens[i]], 18); + + _assertTokenPriceFeedConfigNotConfigured( + s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[i].sourceToken) + ); + + vm.expectEmit(); + emit FeeQuoter.PriceFeedPerTokenUpdated(tokenPriceFeedUpdates[i].sourceToken, tokenPriceFeedUpdates[i].feedConfig); + } + + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + _assertTokenPriceFeedConfigEquality( + s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig + ); + _assertTokenPriceFeedConfigEquality( + s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[1].sourceToken), tokenPriceFeedUpdates[1].feedConfig + ); + } + + function test_FeedUnset_Success() public { + Internal.TimestampedPackedUint224 memory priceQueryInitial = s_feeQuoter.getTokenPrice(s_sourceTokens[0]); + assertFalse(priceQueryInitial.value == 0); + assertFalse(priceQueryInitial.timestamp == 0); + + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = + _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); + + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + _assertTokenPriceFeedConfigEquality( + s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig + ); + + tokenPriceFeedUpdates[0].feedConfig.dataFeedAddress = address(0); + vm.expectEmit(); + emit FeeQuoter.PriceFeedPerTokenUpdated(tokenPriceFeedUpdates[0].sourceToken, tokenPriceFeedUpdates[0].feedConfig); + + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + _assertTokenPriceFeedConfigEquality( + s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig + ); + + // Price data should remain after a feed has been set->unset + Internal.TimestampedPackedUint224 memory priceQueryPostUnsetFeed = s_feeQuoter.getTokenPrice(s_sourceTokens[0]); + assertEq(priceQueryPostUnsetFeed.value, priceQueryInitial.value); + assertEq(priceQueryPostUnsetFeed.timestamp, priceQueryInitial.timestamp); + } + + function test_FeedNotUpdated() public { + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = + _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); + + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + + _assertTokenPriceFeedConfigEquality( + s_feeQuoter.getTokenPriceFeedConfig(tokenPriceFeedUpdates[0].sourceToken), tokenPriceFeedUpdates[0].feedConfig + ); + } + + // Reverts + + function test_FeedUpdatedByNonOwner_Revert() public { + FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](1); + tokenPriceFeedUpdates[0] = + _getSingleTokenPriceFeedUpdateStruct(s_sourceTokens[0], s_dataFeedByToken[s_sourceTokens[0]], 18); + + vm.startPrank(STRANGER); + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + + s_feeQuoter.updateTokenPriceFeeds(tokenPriceFeedUpdates); + } + + function _assertTokenPriceFeedConfigNotConfigured( + FeeQuoter.TokenPriceFeedConfig memory config + ) internal pure virtual { + _assertTokenPriceFeedConfigEquality( + config, FeeQuoter.TokenPriceFeedConfig({dataFeedAddress: address(0), tokenDecimals: 0, isEnabled: false}) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.validateDestFamilyAddress.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.validateDestFamilyAddress.t.sol new file mode 100644 index 00000000000..761cb7546a9 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoter.validateDestFamilyAddress.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../libraries/Internal.sol"; +import {FeeQuoterSetup} from "./FeeQuoterSetup.t.sol"; + +contract FeeQuoter_validateDestFamilyAddress is FeeQuoterSetup { + function test_ValidEVMAddress_Success() public view { + bytes memory encodedAddress = abi.encode(address(10000)); + s_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, encodedAddress); + } + + function test_ValidNonEVMAddress_Success() public view { + s_feeQuoter.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_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, invalidAddress); + } + + function test_InvalidEVMAddressEncodePacked_Revert() public { + bytes memory invalidAddress = abi.encodePacked(address(234)); + vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, invalidAddress)); + s_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, invalidAddress); + } + + function test_InvalidEVMAddressPrecompiles_Revert() public { + for (uint160 i = 0; i < Internal.PRECOMPILE_SPACE; ++i) { + bytes memory invalidAddress = abi.encode(address(i)); + vm.expectRevert(abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, invalidAddress)); + s_feeQuoter.validateDestFamilyAddress(Internal.CHAIN_FAMILY_SELECTOR_EVM, invalidAddress); + } + + s_feeQuoter.validateDestFamilyAddress( + Internal.CHAIN_FAMILY_SELECTOR_EVM, abi.encode(address(uint160(Internal.PRECOMPILE_SPACE))) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol index d8a9a06bb35..a6551c554e6 100644 --- a/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/feeQuoter/FeeQuoterSetup.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {IFeeQuoter} from "../../interfaces/IFeeQuoter.sol"; - import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol"; import {FeeQuoter} from "../../FeeQuoter.sol"; import {Client} from "../../libraries/Client.sol"; @@ -15,11 +13,26 @@ contract FeeQuoterSetup is TokenSetup { uint112 internal constant USD_PER_GAS = 1e6; // 0.001 gwei uint112 internal constant USD_PER_DATA_AVAILABILITY_GAS = 1e9; // 1 gwei + address internal constant DUMMY_CONTRACT_ADDRESS = 0x1111111111111111111111111111111111111112; address internal constant CUSTOM_TOKEN = address(12345); address internal constant CUSTOM_TOKEN_2 = address(bytes20(keccak256("CUSTOM_TOKEN_2"))); + // Use 16 gas per data availability byte in our tests. + // This is an overestimation in OP stack, it ignores 4 gas per 0 byte rule. + // Arbitrum on the other hand, does always use 16 gas per data availability byte. + // This value may be substantially decreased after EIP 4844. + uint16 internal constant DEST_GAS_PER_DATA_AVAILABILITY_BYTE = 16; + + // Total L1 data availability overhead estimate is 33_596 gas. + // This value includes complete CommitStore and OffRamp call data. + uint32 internal constant DEST_DATA_AVAILABILITY_OVERHEAD_GAS = 188 // Fixed data availability overhead in OP stack. + + (32 * 31 + 4) * DEST_GAS_PER_DATA_AVAILABILITY_BYTE // CommitStore single-root transmission takes up about 31 slots, plus selector. + + (32 * 34 + 4) * DEST_GAS_PER_DATA_AVAILABILITY_BYTE; // OffRamp transmission excluding EVM2EVMMessage takes up about 34 slots, plus selector. + + // Multiples of bps, or 0.0001, use 6840 to be same as OP mainnet compression factor of 0.684. + uint16 internal constant DEST_GAS_DATA_AVAILABILITY_MULTIPLIER_BPS = 6840; + uint224 internal constant CUSTOM_TOKEN_PRICE = 1e17; // $0.1 CUSTOM - uint224 internal constant CUSTOM_TOKEN_PRICE_2 = 1e18; // $1 CUSTOM // Encode L1 gas price and L2 gas price into a packed price. // L1 gas price is left-shifted to the higher-order bits. @@ -33,8 +46,6 @@ contract FeeQuoterSetup is TokenSetup { address[] internal s_sourceFeeTokens; uint224[] internal s_sourceTokenPrices; - address[] internal s_destFeeTokens; - uint224[] internal s_destTokenPrices; FeeQuoter.PremiumMultiplierWeiPerEthArgs[] internal s_feeQuoterPremiumMultiplierWeiPerEthArgs; FeeQuoter.TokenTransferFeeConfigArgs[] internal s_feeQuoterTokenTransferFeeConfigArgs; @@ -42,7 +53,7 @@ contract FeeQuoterSetup is TokenSetup { mapping(address token => address dataFeedAddress) internal s_dataFeedByToken; function setUp() public virtual override { - TokenSetup.setUp(); + super.setUp(); _deployTokenPriceDataFeed(s_sourceFeeToken, 8, 1e8); @@ -65,13 +76,11 @@ contract FeeQuoterSetup is TokenSetup { destFeeTokens[0] = s_destTokens[0]; destFeeTokens[1] = s_destTokens[1]; destFeeTokens[2] = s_destRouter.getWrappedNative(); - s_destFeeTokens = destFeeTokens; uint224[] memory destTokenPrices = new uint224[](3); destTokenPrices[0] = 5e18; destTokenPrices[1] = 2000e18; destTokenPrices[2] = 2000e18; - s_destTokenPrices = destTokenPrices; uint256 sourceTokenCount = sourceFeeTokens.length; uint256 destTokenCount = destFeeTokens.length; @@ -98,7 +107,6 @@ contract FeeQuoterSetup is TokenSetup { address[] memory feeTokens = new address[](2); feeTokens[0] = s_sourceTokens[0]; feeTokens[1] = s_weth; - FeeQuoter.TokenPriceFeedUpdate[] memory tokenPriceFeedUpdates = new FeeQuoter.TokenPriceFeedUpdate[](0); s_feeQuoterPremiumMultiplierWeiPerEthArgs.push( FeeQuoter.PremiumMultiplierWeiPerEthArgs({ @@ -166,7 +174,7 @@ contract FeeQuoterSetup is TokenSetup { }), priceUpdaters, feeTokens, - tokenPriceFeedUpdates, + new FeeQuoter.TokenPriceFeedUpdate[](0), s_feeQuoterTokenTransferFeeConfigArgs, s_feeQuoterPremiumMultiplierWeiPerEthArgs, _generateFeeQuoterDestChainConfigArgs() @@ -196,13 +204,6 @@ contract FeeQuoterSetup is TokenSetup { return priceUpdates; } - function _getEmptyPriceUpdates() internal pure returns (Internal.PriceUpdates memory priceUpdates) { - return Internal.PriceUpdates({ - tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), - gasPriceUpdates: new Internal.GasPriceUpdate[](0) - }); - } - function _getSingleTokenPriceFeedUpdateStruct( address sourceToken, address dataFeedAddress, @@ -275,14 +276,6 @@ contract FeeQuoterSetup is TokenSetup { assertEq(config1.isEnabled, config2.isEnabled); } - function _assertTokenPriceFeedConfigNotConfigured( - FeeQuoter.TokenPriceFeedConfig memory config - ) internal pure virtual { - _assertTokenPriceFeedConfigEquality( - config, FeeQuoter.TokenPriceFeedConfig({dataFeedAddress: address(0), tokenDecimals: 0, isEnabled: false}) - ); - } - function _assertTokenTransferFeeConfigEqual( FeeQuoter.TokenTransferFeeConfig memory a, FeeQuoter.TokenTransferFeeConfig memory b @@ -295,14 +288,6 @@ contract FeeQuoterSetup is TokenSetup { assertEq(a.isEnabled, b.isEnabled); } - function _assertFeeQuoterStaticConfigsEqual( - FeeQuoter.StaticConfig memory a, - FeeQuoter.StaticConfig memory b - ) internal pure { - assertEq(a.linkToken, b.linkToken); - assertEq(a.maxFeeJuelsPerMsg, b.maxFeeJuelsPerMsg); - } - function _assertFeeQuoterDestChainConfigsEqual( FeeQuoter.DestChainConfig memory a, FeeQuoter.DestChainConfig memory b @@ -325,19 +310,12 @@ contract FeeQuoterSetup is TokenSetup { contract FeeQuoterFeeSetup is FeeQuoterSetup { uint224 internal s_feeTokenPrice; uint224 internal s_wrappedTokenPrice; - uint224 internal s_customTokenPrice; - - address internal s_selfServeTokenDefaultPricing = makeAddr("self-serve-token-default-pricing"); - - address internal s_destTokenPool = makeAddr("destTokenPool"); - address internal s_destToken = makeAddr("destToken"); function setUp() public virtual override { super.setUp(); s_feeTokenPrice = s_sourceTokenPrices[0]; s_wrappedTokenPrice = s_sourceTokenPrices[2]; - s_customTokenPrice = CUSTOM_TOKEN_PRICE; s_feeQuoter.updatePrices(_getSingleTokenPriceUpdateStruct(CUSTOM_TOKEN, CUSTOM_TOKEN_PRICE)); } @@ -368,48 +346,6 @@ contract FeeQuoterFeeSetup is FeeQuoterSetup { }); } - function _messageToEvent( - Client.EVM2AnyMessage memory message, - uint64 sourceChainSelector, - uint64 destChainSelector, - uint64 seqNum, - uint64 nonce, - uint256 feeTokenAmount, - uint256 feeValueJuels, - address originalSender, - bytes32 metadataHash, - TokenAdminRegistry tokenAdminRegistry - ) internal view returns (Internal.EVM2AnyRampMessage memory) { - Client.EVMExtraArgsV2 memory extraArgs = - s_feeQuoter.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, - data: message.data, - receiver: message.receiver, - extraArgs: Client._argsToBytes(extraArgs), - feeToken: message.feeToken, - feeTokenAmount: feeTokenAmount, - feeValueJuels: feeValueJuels, - tokenAmounts: new Internal.EVM2AnyTokenTransfer[](message.tokenAmounts.length) - }); - - for (uint256 i = 0; i < message.tokenAmounts.length; ++i) { - messageEvent.tokenAmounts[i] = - _getSourceTokenData(message.tokenAmounts[i], tokenAdminRegistry, DEST_CHAIN_SELECTOR); - } - - messageEvent.header.messageId = Internal._hash(messageEvent, metadataHash); - return messageEvent; - } - function _getSourceTokenData( Client.EVMTokenAmount memory tokenAmount, TokenAdminRegistry tokenAdminRegistry, @@ -432,14 +368,6 @@ contract FeeQuoterFeeSetup is FeeQuoterSetup { }); } - function _calcUSDValueFromTokenAmount(uint224 tokenPrice, uint256 tokenAmount) internal pure returns (uint256) { - return (tokenPrice * tokenAmount) / 1e18; - } - - function _applyBpsRatio(uint256 tokenAmount, uint16 ratio) internal pure returns (uint256) { - return (tokenAmount * ratio) / 1e5; - } - function _configUSDCentToWei( uint256 usdCent ) internal pure returns (uint256) { diff --git a/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol b/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol index 6cbe7bf58f4..cd0aabf1776 100644 --- a/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol +++ b/contracts/src/v0.8/ccip/test/mocks/test/MockRouterTest.t.sol @@ -13,7 +13,7 @@ contract MockRouterTest is TokenSetup { MockCCIPRouter public mockRouter; - uint64 public constant mockChainSelector = 123456; + uint64 public constant MOCK_CHAIN_SELECTOR = 123456; Client.EVM2AnyMessage public message; @@ -34,26 +34,26 @@ contract MockRouterTest is TokenSetup { function test_ccipSendWithInsufficientNativeTokens_Revert() public { //Should revert because did not include sufficient eth to pay for fees vm.expectRevert(IRouterClient.InsufficientFeeTokenAmount.selector); - mockRouter.ccipSend(mockChainSelector, message); + mockRouter.ccipSend(MOCK_CHAIN_SELECTOR, message); } function test_ccipSendWithSufficientNativeFeeTokens_Success() public { //ccipSend with sufficient native tokens for fees - mockRouter.ccipSend{value: 0.1 ether}(mockChainSelector, message); + mockRouter.ccipSend{value: 0.1 ether}(MOCK_CHAIN_SELECTOR, message); } function test_ccipSendWithInvalidMsgValue_Revert() public { message.feeToken = address(1); //Set to non native-token fees vm.expectRevert(IRouterClient.InvalidMsgValue.selector); - mockRouter.ccipSend{value: 0.1 ether}(mockChainSelector, message); + mockRouter.ccipSend{value: 0.1 ether}(MOCK_CHAIN_SELECTOR, message); } function test_ccipSendWithLinkFeeTokenbutInsufficientAllowance_Revert() public { message.feeToken = s_sourceFeeToken; vm.expectRevert(bytes("ERC20: insufficient allowance")); - mockRouter.ccipSend(mockChainSelector, message); + mockRouter.ccipSend(MOCK_CHAIN_SELECTOR, message); } function test_ccipSendWithLinkFeeTokenAndValidMsgValue_Success() public { @@ -63,6 +63,6 @@ contract MockRouterTest is TokenSetup { IERC20(s_sourceFeeToken).safeApprove(address(mockRouter), type(uint256).max); - mockRouter.ccipSend(mockChainSelector, message); + mockRouter.ccipSend(MOCK_CHAIN_SELECTOR, message); } } diff --git a/contracts/src/v0.8/ccip/test/offRamp/OffRamp.t.sol b/contracts/src/v0.8/ccip/test/offRamp/OffRamp.t.sol deleted file mode 100644 index 1601c64d1b1..00000000000 --- a/contracts/src/v0.8/ccip/test/offRamp/OffRamp.t.sol +++ /dev/null @@ -1,3850 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IFeeQuoter} from "../../interfaces/IFeeQuoter.sol"; -import {IMessageInterceptor} from "../../interfaces/IMessageInterceptor.sol"; -import {IRMNRemote} from "../../interfaces/IRMNRemote.sol"; -import {IRouter} from "../../interfaces/IRouter.sol"; -import {ITokenAdminRegistry} from "../../interfaces/ITokenAdminRegistry.sol"; - -import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; -import {CallWithExactGas} from "../../../shared/call/CallWithExactGas.sol"; -import {FeeQuoter} from "../../FeeQuoter.sol"; -import {NonceManager} from "../../NonceManager.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {MultiOCR3Base} from "../../ocr/MultiOCR3Base.sol"; -import {OffRamp} from "../../offRamp/OffRamp.sol"; -import {LockReleaseTokenPool} from "../../pools/LockReleaseTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; -import {OffRampHelper} from "../helpers/OffRampHelper.sol"; -import {ConformingReceiver} from "../helpers/receivers/ConformingReceiver.sol"; -import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; -import {MaybeRevertMessageReceiverNo165} from "../helpers/receivers/MaybeRevertMessageReceiverNo165.sol"; -import {ReentrancyAbuserMultiRamp} from "../helpers/receivers/ReentrancyAbuserMultiRamp.sol"; -import {OffRampSetup} from "./OffRampSetup.t.sol"; -import {Vm} from "forge-std/Vm.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract OffRamp_constructor is OffRampSetup { - function test_Constructor_Success() public { - OffRamp.StaticConfig memory staticConfig = OffRamp.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - tokenAdminRegistry: address(s_tokenAdminRegistry), - nonceManager: address(s_inboundNonceManager) - }); - OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](2); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - sourceChainConfigs[1] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 1, - onRamp: ON_RAMP_ADDRESS_2, - isEnabled: true - }); - - OffRamp.SourceChainConfig memory expectedSourceChainConfig1 = OffRamp.SourceChainConfig({ - router: s_destRouter, - isEnabled: true, - minSeqNr: 1, - onRamp: sourceChainConfigs[0].onRamp - }); - - OffRamp.SourceChainConfig memory expectedSourceChainConfig2 = OffRamp.SourceChainConfig({ - router: s_destRouter, - isEnabled: true, - minSeqNr: 1, - onRamp: sourceChainConfigs[1].onRamp - }); - - uint64[] memory expectedSourceChainSelectors = new uint64[](2); - expectedSourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; - expectedSourceChainSelectors[1] = SOURCE_CHAIN_SELECTOR_1 + 1; - - vm.expectEmit(); - emit OffRamp.StaticConfigSet(staticConfig); - - vm.expectEmit(); - emit OffRamp.DynamicConfigSet(dynamicConfig); - - vm.expectEmit(); - emit OffRamp.SourceChainSelectorAdded(SOURCE_CHAIN_SELECTOR_1); - - vm.expectEmit(); - emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1, expectedSourceChainConfig1); - - vm.expectEmit(); - emit OffRamp.SourceChainSelectorAdded(SOURCE_CHAIN_SELECTOR_1 + 1); - - vm.expectEmit(); - emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1 + 1, expectedSourceChainConfig2); - - s_offRamp = new OffRampHelper(staticConfig, dynamicConfig, sourceChainConfigs); - - MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); - ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ - ocrPluginType: uint8(Internal.OCRPluginType.Execution), - configDigest: s_configDigestExec, - F: s_F, - isSignatureVerificationEnabled: false, - signers: s_emptySigners, - transmitters: s_validTransmitters - }); - - s_offRamp.setOCR3Configs(ocrConfigs); - - // Static config - OffRamp.StaticConfig memory gotStaticConfig = s_offRamp.getStaticConfig(); - assertEq(staticConfig.chainSelector, gotStaticConfig.chainSelector); - assertEq(address(staticConfig.rmnRemote), address(gotStaticConfig.rmnRemote)); - assertEq(staticConfig.tokenAdminRegistry, gotStaticConfig.tokenAdminRegistry); - - // Dynamic config - OffRamp.DynamicConfig memory gotDynamicConfig = s_offRamp.getDynamicConfig(); - _assertSameConfig(dynamicConfig, gotDynamicConfig); - - // OCR Config - MultiOCR3Base.OCRConfig memory expectedOCRConfig = MultiOCR3Base.OCRConfig({ - configInfo: MultiOCR3Base.ConfigInfo({ - configDigest: ocrConfigs[0].configDigest, - F: ocrConfigs[0].F, - n: 0, - isSignatureVerificationEnabled: ocrConfigs[0].isSignatureVerificationEnabled - }), - signers: s_emptySigners, - transmitters: s_validTransmitters - }); - MultiOCR3Base.OCRConfig memory gotOCRConfig = s_offRamp.latestConfigDetails(uint8(Internal.OCRPluginType.Execution)); - _assertOCRConfigEquality(expectedOCRConfig, gotOCRConfig); - - (uint64[] memory actualSourceChainSelectors, OffRamp.SourceChainConfig[] memory actualSourceChainConfigs) = - s_offRamp.getAllSourceChainConfigs(); - - _assertSourceChainConfigEquality(actualSourceChainConfigs[0], expectedSourceChainConfig1); - _assertSourceChainConfigEquality(actualSourceChainConfigs[1], expectedSourceChainConfig2); - - // OffRamp initial values - assertEq("OffRamp 1.6.0-dev", s_offRamp.typeAndVersion()); - assertEq(OWNER, s_offRamp.owner()); - assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); - - // assertion for source chain selector - for (uint256 i = 0; i < expectedSourceChainSelectors.length; i++) { - assertEq(expectedSourceChainSelectors[i], actualSourceChainSelectors[i]); - } - } - - // Revert - function test_ZeroOnRampAddress_Revert() public { - uint64[] memory sourceChainSelectors = new uint64[](1); - sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: new bytes(0), - isEnabled: true - }); - - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - - s_offRamp = new OffRampHelper( - OffRamp.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - tokenAdminRegistry: address(s_tokenAdminRegistry), - nonceManager: address(s_inboundNonceManager) - }), - _generateDynamicOffRampConfig(address(s_feeQuoter)), - sourceChainConfigs - ); - } - - function test_SourceChainSelector_Revert() public { - uint64[] memory sourceChainSelectors = new uint64[](1); - sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: 0, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - vm.expectRevert(OffRamp.ZeroChainSelectorNotAllowed.selector); - - s_offRamp = new OffRampHelper( - OffRamp.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - tokenAdminRegistry: address(s_tokenAdminRegistry), - nonceManager: address(s_inboundNonceManager) - }), - _generateDynamicOffRampConfig(address(s_feeQuoter)), - sourceChainConfigs - ); - } - - function test_ZeroRMNRemote_Revert() public { - uint64[] memory sourceChainSelectors = new uint64[](1); - sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); - - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - - s_offRamp = new OffRampHelper( - OffRamp.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - rmnRemote: IRMNRemote(ZERO_ADDRESS), - tokenAdminRegistry: address(s_tokenAdminRegistry), - nonceManager: address(s_inboundNonceManager) - }), - _generateDynamicOffRampConfig(address(s_feeQuoter)), - sourceChainConfigs - ); - } - - function test_ZeroChainSelector_Revert() public { - uint64[] memory sourceChainSelectors = new uint64[](1); - sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); - - vm.expectRevert(OffRamp.ZeroChainSelectorNotAllowed.selector); - - s_offRamp = new OffRampHelper( - OffRamp.StaticConfig({ - chainSelector: 0, - rmnRemote: s_mockRMNRemote, - tokenAdminRegistry: address(s_tokenAdminRegistry), - nonceManager: address(s_inboundNonceManager) - }), - _generateDynamicOffRampConfig(address(s_feeQuoter)), - sourceChainConfigs - ); - } - - function test_ZeroTokenAdminRegistry_Revert() public { - uint64[] memory sourceChainSelectors = new uint64[](1); - sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); - - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - - s_offRamp = new OffRampHelper( - OffRamp.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - tokenAdminRegistry: ZERO_ADDRESS, - nonceManager: address(s_inboundNonceManager) - }), - _generateDynamicOffRampConfig(address(s_feeQuoter)), - sourceChainConfigs - ); - } - - function test_ZeroNonceManager_Revert() public { - uint64[] memory sourceChainSelectors = new uint64[](1); - sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); - - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - - s_offRamp = new OffRampHelper( - OffRamp.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - tokenAdminRegistry: address(s_tokenAdminRegistry), - nonceManager: ZERO_ADDRESS - }), - _generateDynamicOffRampConfig(address(s_feeQuoter)), - sourceChainConfigs - ); - } -} - -contract OffRamp_setDynamicConfig is OffRampSetup { - function test_SetDynamicConfig_Success() public { - OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); - - vm.expectEmit(); - emit OffRamp.DynamicConfigSet(dynamicConfig); - - s_offRamp.setDynamicConfig(dynamicConfig); - - OffRamp.DynamicConfig memory newConfig = s_offRamp.getDynamicConfig(); - _assertSameConfig(dynamicConfig, newConfig); - } - - function test_SetDynamicConfigWithInterceptor_Success() public { - OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); - dynamicConfig.messageInterceptor = address(s_inboundMessageInterceptor); - - vm.expectEmit(); - emit OffRamp.DynamicConfigSet(dynamicConfig); - - s_offRamp.setDynamicConfig(dynamicConfig); - - OffRamp.DynamicConfig memory newConfig = s_offRamp.getDynamicConfig(); - _assertSameConfig(dynamicConfig, newConfig); - } - - // Reverts - - function test_NonOwner_Revert() public { - vm.startPrank(STRANGER); - OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); - - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - - s_offRamp.setDynamicConfig(dynamicConfig); - } - - function test_FeeQuoterZeroAddress_Revert() public { - OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(ZERO_ADDRESS); - - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - - s_offRamp.setDynamicConfig(dynamicConfig); - } -} - -contract OffRamp_ccipReceive is OffRampSetup { - // Reverts - - function test_Reverts() public { - Client.Any2EVMMessage memory message = - _convertToGeneralMessage(_generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1)); - vm.expectRevert(); - s_offRamp.ccipReceive(message); - } -} - -contract OffRamp_executeSingleReport is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); - } - - function test_SingleMessageNoTokens_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - messages[0].header.nonce++; - messages[0].header.sequenceNumber++; - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); - } - - function test_SingleMessageNoTokensUnordered_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].header.nonce = 0; - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - // Nonce never increments on unordered messages. - uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertEq( - s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), - nonceBefore, - "nonce must remain unchanged on unordered messages" - ); - - messages[0].header.sequenceNumber++; - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - // Nonce never increments on unordered messages. - nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - assertEq( - s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), - nonceBefore, - "nonce must remain unchanged on unordered messages" - ); - } - - function test_SingleMessageNoTokensOtherChain_Success() public { - Internal.Any2EVMRampMessage[] memory messagesChain1 = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesChain1), new OffRamp.GasLimitOverride[](0) - ); - - uint64 nonceChain1 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender); - assertGt(nonceChain1, 0); - - Internal.Any2EVMRampMessage[] memory messagesChain2 = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); - assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); - - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messagesChain2), new OffRamp.GasLimitOverride[](0) - ); - assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); - - // Other chain's nonce is unaffected - assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender), nonceChain1); - } - - function test_ReceiverError_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - bytes memory realError1 = new bytes(2); - realError1[0] = 0xbe; - realError1[1] = 0xef; - s_reverting_receiver.setErr(realError1); - - messages[0].receiver = address(s_reverting_receiver); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - // Nonce should increment on non-strict - assertEq(uint64(0), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - OffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, realError1) - ) - ); - assertEq(uint64(1), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); - } - - function test_SkippedIncorrectNonce_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - messages[0].header.nonce++; - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - vm.expectEmit(); - emit NonceManager.SkippedIncorrectNonce( - messages[0].header.sourceChainSelector, messages[0].header.nonce, messages[0].sender - ); - - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - } - - function test_SkippedIncorrectNonceStillExecutes_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - messages[1].header.nonce++; - messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); - - vm.expectEmit(); - emit NonceManager.SkippedIncorrectNonce(SOURCE_CHAIN_SELECTOR_1, messages[1].header.nonce, messages[1].sender); - - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test__execute_SkippedAlreadyExecutedMessage_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - vm.expectEmit(); - emit OffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); - - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - } - - function test__execute_SkippedAlreadyExecutedMessageUnordered_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].header.nonce = 0; - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - vm.expectEmit(); - emit OffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); - - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - } - - // Send a message to a contract that does not implement the CCIPReceiver interface - // This should execute successfully. - function test_SingleMessageToNonCCIPReceiver_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - MaybeRevertMessageReceiverNo165 newReceiver = new MaybeRevertMessageReceiverNo165(true); - messages[0].receiver = address(newReceiver); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_SingleMessagesNoTokensSuccess_gas() public { - vm.pauseGasMetering(); - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.resumeGasMetering(); - vm.recordLogs(); - s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_TwoMessagesWithTokensSuccess_gas() public { - vm.pauseGasMetering(); - Internal.Any2EVMRampMessage[] memory messages = - _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - // Set message 1 to use another receiver to simulate more fair gas costs - messages[1].receiver = address(s_secondary_receiver); - messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); - - Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.resumeGasMetering(); - vm.recordLogs(); - s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[1].header.sequenceNumber, - messages[1].header.messageId, - _hashMessage(messages[1], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_TwoMessagesWithTokensAndGE_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - // Set message 1 to use another receiver to simulate more fair gas costs - messages[1].receiver = address(s_secondary_receiver); - messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); - - assertEq(uint64(0), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); - - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) - ); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[1].header.sequenceNumber, - messages[1].header.messageId, - _hashMessage(messages[1], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - assertEq(uint64(2), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); - } - - function test_Fuzz_InterleavingOrderedAndUnorderedMessages_Success( - bool[7] memory orderings - ) public { - Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](orderings.length); - // number of tokens needs to be capped otherwise we hit UnsupportedNumberOfTokens. - Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](3); - for (uint256 i = 0; i < 3; ++i) { - tokenAmounts[i].token = s_sourceTokens[i % s_sourceTokens.length]; - tokenAmounts[i].amount = 1e18; - } - uint64 expectedNonce = 0; - - for (uint256 i = 0; i < orderings.length; ++i) { - messages[i] = - _generateAny2EVMMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, uint64(i + 1), tokenAmounts, !orderings[i]); - if (orderings[i]) { - messages[i].header.nonce = ++expectedNonce; - } - messages[i].header.messageId = _hashMessage(messages[i], ON_RAMP_ADDRESS_1); - } - - uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER)); - assertEq(uint64(0), nonceBefore, "nonce before exec should be 0"); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) - ); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - // all executions should succeed. - for (uint256 i = 0; i < orderings.length; ++i) { - assertEq( - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, messages[i].header.sequenceNumber)), - uint256(Internal.MessageExecutionState.SUCCESS) - ); - - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[i].header.sequenceNumber, - messages[i].header.messageId, - _hashMessage(messages[i], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - assertEq( - nonceBefore + expectedNonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER)) - ); - } - - function test_InvalidSourcePoolAddress_Success() public { - address fakePoolAddress = address(0x0000000000333333); - - Internal.Any2EVMRampMessage[] memory messages = - _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].tokenAmounts[0].sourcePoolAddress = abi.encode(fakePoolAddress); - - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - OffRamp.TokenHandlingError.selector, - abi.encodeWithSelector(TokenPool.InvalidSourcePoolAddress.selector, abi.encode(fakePoolAddress)) - ) - ); - } - - function test_WithCurseOnAnotherSourceChain_Success() public { - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_2, true); - s_offRamp.executeSingleReport( - _generateReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ), - new OffRamp.GasLimitOverride[](0) - ); - } - - function test_Unhealthy_Success() public { - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); - - vm.expectEmit(); - emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); - s_offRamp.executeSingleReport( - _generateReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ), - new OffRamp.GasLimitOverride[](0) - ); - - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, false); - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ), - new OffRamp.GasLimitOverride[](0) - ); - - _assertNoEmit(OffRamp.SkippedReportExecution.selector); - } - - // Reverts - - function test_MismatchingDestChainSelector_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); - messages[0].header.destChainSelector = DEST_CHAIN_SELECTOR + 1; - - Internal.ExecutionReport memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.expectRevert( - abi.encodeWithSelector(OffRamp.InvalidMessageDestChainSelector.selector, messages[0].header.destChainSelector) - ); - s_offRamp.executeSingleReport(executionReport, new OffRamp.GasLimitOverride[](0)); - } - - function test_UnhealthySingleChainCurse_Revert() public { - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); - vm.expectEmit(); - emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); - s_offRamp.executeSingleReport( - _generateReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ), - new OffRamp.GasLimitOverride[](0) - ); - vm.recordLogs(); - // Uncurse should succeed - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, false); - s_offRamp.executeSingleReport( - _generateReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ), - new OffRamp.GasLimitOverride[](0) - ); - _assertNoEmit(OffRamp.SkippedReportExecution.selector); - } - - function test_UnexpectedTokenData_Revert() public { - Internal.ExecutionReport memory report = _generateReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ); - report.offchainTokenData = new bytes[][](report.messages.length + 1); - - vm.expectRevert(OffRamp.UnexpectedTokenData.selector); - - s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); - } - - function test_EmptyReport_Revert() public { - vm.expectRevert(abi.encodeWithSelector(OffRamp.EmptyReport.selector, SOURCE_CHAIN_SELECTOR_1)); - - s_offRamp.executeSingleReport( - Internal.ExecutionReport({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - proofs: new bytes32[](0), - proofFlagBits: 0, - messages: new Internal.Any2EVMRampMessage[](0), - offchainTokenData: new bytes[][](0) - }), - new OffRamp.GasLimitOverride[](0) - ); - } - - function test_RootNotCommitted_Revert() public { - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 0); - vm.expectRevert(abi.encodeWithSelector(OffRamp.RootNotCommitted.selector, SOURCE_CHAIN_SELECTOR_1)); - - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) - ); - } - - function test_ManualExecutionNotYetEnabled_Revert() public { - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, BLOCK_TIME); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.ManualExecutionNotYetEnabled.selector, SOURCE_CHAIN_SELECTOR_1)); - - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) - ); - } - - function test_NonExistingSourceChain_Revert() public { - uint64 newSourceChainSelector = SOURCE_CHAIN_SELECTOR_1 + 1; - bytes memory newOnRamp = abi.encode(ON_RAMP_ADDRESS, 1); - - Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(newSourceChainSelector, newOnRamp); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.SourceChainNotEnabled.selector, newSourceChainSelector)); - s_offRamp.executeSingleReport( - _generateReportFromMessages(newSourceChainSelector, messages), new OffRamp.GasLimitOverride[](0) - ); - } - - function test_DisabledSourceChain_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.SourceChainNotEnabled.selector, SOURCE_CHAIN_SELECTOR_2)); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new OffRamp.GasLimitOverride[](0) - ); - } - - function test_TokenDataMismatch_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - report.offchainTokenData[0] = new bytes[](messages[0].tokenAmounts.length + 1); - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.TokenDataMismatch.selector, SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber - ) - ); - s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); - } - - function test_RouterYULCall_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - // gas limit too high, Router's external call should revert - messages[0].gasLimit = 1e36; - messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - Internal.ExecutionReport memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.recordLogs(); - s_offRamp.executeSingleReport(executionReport, new OffRamp.GasLimitOverride[](0)); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector(CallWithExactGas.NotEnoughGasForCall.selector) - ); - } - - function test_RetryFailedMessageWithoutManualExecution_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - bytes memory realError1 = new bytes(2); - realError1[0] = 0xbe; - realError1[1] = 0xef; - s_reverting_receiver.setErr(realError1); - - messages[0].receiver = address(s_reverting_receiver); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - OffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, realError1) - ) - ); - - // The second time should skip the msg - vm.expectEmit(); - emit OffRamp.AlreadyAttempted(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); - - s_offRamp.executeSingleReport( - _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) - ); - } - - function _constructCommitReport( - bytes32 merkleRoot - ) internal view returns (OffRamp.CommitReport memory) { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: abi.encode(ON_RAMP_ADDRESS_1), - minSeqNr: 1, - maxSeqNr: 2, - merkleRoot: merkleRoot - }); - - return OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - } -} - -contract OffRamp_executeSingleMessage is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - vm.startPrank(address(s_offRamp)); - } - - function test_executeSingleMessage_NoTokens_Success() public { - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - - Client.Any2EVMMessage memory expectedAny2EvmMessage = Client.Any2EVMMessage({ - messageId: message.header.messageId, - sourceChainSelector: message.header.sourceChainSelector, - sender: message.sender, - data: message.data, - destTokenAmounts: new Client.EVMTokenAmount[](0) - }); - vm.expectCall( - address(s_destRouter), - abi.encodeWithSelector( - IRouter.routeMessage.selector, - expectedAny2EvmMessage, - Internal.GAS_FOR_CALL_EXACT_CHECK, - message.gasLimit, - message.receiver - ) - ); - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_executeSingleMessage_WithTokens_Success() public { - Internal.Any2EVMRampMessage memory message = - _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)[0]; - bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length); - - vm.expectCall( - s_destPoolByToken[s_destTokens[0]], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: message.sender, - receiver: message.receiver, - amount: message.tokenAmounts[0].amount, - localToken: message.tokenAmounts[0].destTokenAddress, - remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: message.tokenAmounts[0].sourcePoolAddress, - sourcePoolData: message.tokenAmounts[0].extraData, - offchainTokenData: offchainTokenData[0] - }) - ) - ); - - s_offRamp.executeSingleMessage(message, offchainTokenData, new uint32[](0)); - } - - function test_executeSingleMessage_WithVInterception_Success() public { - vm.stopPrank(); - vm.startPrank(OWNER); - _enableInboundMessageInterceptor(); - vm.startPrank(address(s_offRamp)); - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_NonContract_Success() public { - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - message.receiver = STRANGER; - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_NonContractWithTokens_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - vm.expectEmit(); - emit TokenPool.Released(address(s_offRamp), STRANGER, amounts[0]); - vm.expectEmit(); - emit TokenPool.Minted(address(s_offRamp), STRANGER, amounts[1]); - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - message.receiver = STRANGER; - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - // Reverts - - function test_TokenHandlingError_Revert() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - bytes memory errorMessage = "Random token pool issue"; - - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - s_maybeRevertingPool.setShouldRevert(errorMessage); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, errorMessage)); - - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_ZeroGasDONExecution_Revert() public { - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - message.gasLimit = 0; - - vm.expectRevert(abi.encodeWithSelector(OffRamp.ReceiverError.selector, "")); - - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_MessageSender_Revert() public { - vm.stopPrank(); - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - vm.expectRevert(OffRamp.CanOnlySelfCall.selector); - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_executeSingleMessage_WithFailingValidation_Revert() public { - vm.stopPrank(); - vm.startPrank(OWNER); - _enableInboundMessageInterceptor(); - vm.startPrank(address(s_offRamp)); - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - s_inboundMessageInterceptor.setMessageIdValidationState(message.header.messageId, true); - vm.expectRevert( - abi.encodeWithSelector( - IMessageInterceptor.MessageValidationError.selector, - abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) - ) - ); - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } - - function test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() public { - vm.stopPrank(); - vm.startPrank(OWNER); - _enableInboundMessageInterceptor(); - vm.startPrank(address(s_offRamp)); - - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - - // Setup the receiver to a non-CCIP Receiver, which will skip the Router call (but should still perform the validation) - MaybeRevertMessageReceiverNo165 newReceiver = new MaybeRevertMessageReceiverNo165(true); - message.receiver = address(newReceiver); - message.header.messageId = _hashMessage(message, ON_RAMP_ADDRESS_1); - - s_inboundMessageInterceptor.setMessageIdValidationState(message.header.messageId, true); - vm.expectRevert( - abi.encodeWithSelector( - IMessageInterceptor.MessageValidationError.selector, - abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) - ) - ); - s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - } -} - -contract OffRamp_batchExecute is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); - } - - function test_SingleReport_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); - - vm.recordLogs(); - s_offRamp.batchExecute( - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) - ); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); - } - - function test_MultipleReportsSameChain_Success() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); - - uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); - vm.recordLogs(); - s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - assertExecutionStateChangedEventLogs( - logs, - messages1[0].header.sourceChainSelector, - messages1[0].header.sequenceNumber, - messages1[0].header.messageId, - _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - messages1[1].header.sourceChainSelector, - messages1[1].header.sequenceNumber, - messages1[1].header.messageId, - _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - messages2[0].header.sourceChainSelector, - messages2[0].header.sequenceNumber, - messages2[0].header.messageId, - _hashMessage(messages2[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender), nonceBefore); - } - - function test_MultipleReportsDifferentChains_Success() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); - - vm.recordLogs(); - - s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - assertExecutionStateChangedEventLogs( - logs, - messages1[0].header.sourceChainSelector, - messages1[0].header.sequenceNumber, - messages1[0].header.messageId, - _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - messages1[1].header.sourceChainSelector, - messages1[1].header.sequenceNumber, - messages1[1].header.messageId, - _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - messages2[0].header.sourceChainSelector, - messages2[0].header.sequenceNumber, - messages2[0].header.messageId, - _hashMessage(messages2[0], ON_RAMP_ADDRESS_3), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - uint64 nonceChain1 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); - uint64 nonceChain3 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messages2[0].sender); - - assertTrue(nonceChain1 != nonceChain3); - assertGt(nonceChain1, 0); - assertGt(nonceChain3, 0); - } - - function test_MultipleReportsDifferentChainsSkipCursedChain_Success() public { - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); - - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); - - vm.recordLogs(); - - vm.expectEmit(); - emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); - - s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - for (uint256 i = 0; i < logs.length; ++i) { - if (logs[i].topics[0] == OffRamp.ExecutionStateChanged.selector) { - uint64 logSourceChainSelector = uint64(uint256(logs[i].topics[1])); - uint64 logSequenceNumber = uint64(uint256(logs[i].topics[2])); - bytes32 logMessageId = bytes32(logs[i].topics[3]); - (bytes32 logMessageHash, uint8 logState,,) = abi.decode(logs[i].data, (bytes32, uint8, bytes, uint256)); - assertEq(logMessageId, messages2[0].header.messageId); - assertEq(logSourceChainSelector, messages2[0].header.sourceChainSelector); - assertEq(logSequenceNumber, messages2[0].header.sequenceNumber); - assertEq(logMessageId, messages2[0].header.messageId); - assertEq(logMessageHash, _hashMessage(messages2[0], ON_RAMP_ADDRESS_3)); - assertEq(logState, uint8(Internal.MessageExecutionState.SUCCESS)); - } - } - } - - function test_MultipleReportsSkipDuplicate_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.expectEmit(); - emit OffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); - - vm.recordLogs(); - s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); - assertExecutionStateChangedEventLogs( - messages[0].header.sourceChainSelector, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_Unhealthy_Success() public { - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); - vm.expectEmit(); - emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); - s_offRamp.batchExecute( - _generateBatchReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ), - new OffRamp.GasLimitOverride[][](1) - ); - - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, false); - - vm.recordLogs(); - s_offRamp.batchExecute( - _generateBatchReportFromMessages( - SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) - ), - new OffRamp.GasLimitOverride[][](1) - ); - - _assertNoEmit(OffRamp.SkippedReportExecution.selector); - } - - // Reverts - function test_ZeroReports_Revert() public { - vm.expectRevert(OffRamp.EmptyBatch.selector); - s_offRamp.batchExecute(new Internal.ExecutionReport[](0), new OffRamp.GasLimitOverride[][](1)); - } - - function test_OutOfBoundsGasLimitsAccess_Revert() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); - - vm.expectRevert(); - s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](1)); - } -} - -contract OffRamp_manuallyExecute is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); - } - - function test_manuallyExecute_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].receiver = address(s_reverting_receiver); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - s_offRamp.batchExecute( - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) - ); - - s_reverting_receiver.setRevert(false); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](messages.length); - - vm.recordLogs(); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_manuallyExecute_WithGasOverride_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].receiver = address(s_reverting_receiver); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - s_offRamp.batchExecute( - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) - ); - - s_reverting_receiver.setRevert(false); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - gasLimitOverrides[0][0].receiverExecutionGasLimit += 1; - vm.recordLogs(); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_manuallyExecute_DoesNotRevertIfUntouched_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].receiver = address(s_reverting_receiver); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - assertEq( - messages[0].header.nonce - 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender) - ); - - s_reverting_receiver.setRevert(true); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - - vm.recordLogs(); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - OffRamp.ReceiverError.selector, abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, "") - ) - ); - - assertEq( - messages[0].header.nonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender) - ); - } - - function test_manuallyExecute_WithMultiReportGasOverride_Success() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](3); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](2); - - for (uint64 i = 0; i < 3; ++i) { - messages1[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); - messages1[i].receiver = address(s_reverting_receiver); - messages1[i].header.messageId = _hashMessage(messages1[i], ON_RAMP_ADDRESS_1); - } - - for (uint64 i = 0; i < 2; ++i) { - messages2[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, i + 1); - messages2[i].receiver = address(s_reverting_receiver); - messages2[i].header.messageId = _hashMessage(messages2[i], ON_RAMP_ADDRESS_3); - } - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); - - s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); - - s_reverting_receiver.setRevert(false); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages1); - gasLimitOverrides[1] = _getGasLimitsFromMessages(messages2); - - for (uint256 i = 0; i < 3; ++i) { - gasLimitOverrides[0][i].receiverExecutionGasLimit += 1; - } - - for (uint256 i = 0; i < 2; ++i) { - gasLimitOverrides[1][i].receiverExecutionGasLimit += 1; - } - - vm.recordLogs(); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - for (uint256 j = 0; j < 3; ++j) { - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages1[j].header.sequenceNumber, - messages1[j].header.messageId, - _hashMessage(messages1[j], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - for (uint256 k = 0; k < 2; ++k) { - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_3, - messages2[k].header.sequenceNumber, - messages2[k].header.messageId, - _hashMessage(messages2[k], ON_RAMP_ADDRESS_3), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - } - - function test_manuallyExecute_WithPartialMessages_Success() public { - Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](3); - - for (uint64 i = 0; i < 3; ++i) { - messages[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); - } - - messages[1].receiver = address(s_reverting_receiver); - messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - s_offRamp.batchExecute( - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) - ); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[1].header.sequenceNumber, - messages[1].header.messageId, - _hashMessage(messages[1], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - OffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, bytes("")) - ) - ); - - assertExecutionStateChangedEventLogs( - logs, - SOURCE_CHAIN_SELECTOR_1, - messages[2].header.sequenceNumber, - messages[2].header.messageId, - _hashMessage(messages[2], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - s_reverting_receiver.setRevert(false); - - // Only the 2nd message reverted - Internal.Any2EVMRampMessage[] memory newMessages = new Internal.Any2EVMRampMessage[](1); - newMessages[0] = messages[1]; - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(newMessages); - gasLimitOverrides[0][0].receiverExecutionGasLimit += 1; - - vm.recordLogs(); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, newMessages), gasLimitOverrides); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_manuallyExecute_LowGasLimit_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - messages[0].gasLimit = 1; - messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - vm.recordLogs(); - s_offRamp.batchExecute( - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) - ); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector(OffRamp.ReceiverError.selector, "") - ); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](1); - gasLimitOverrides[0][0].receiverExecutionGasLimit = 100_000; - - vm.expectEmit(); - emit ConformingReceiver.MessageReceived(); - - vm.recordLogs(); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - // Reverts - - function test_manuallyExecute_ForkedChain_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - uint256 chain1 = block.chainid; - uint256 chain2 = chain1 + 1; - vm.chainId(chain2); - vm.expectRevert(abi.encodeWithSelector(MultiOCR3Base.ForkedChain.selector, chain1, chain2)); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - } - - function test_ManualExecGasLimitMismatchSingleReport_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](2); - messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - - Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - // No overrides for report - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, new OffRamp.GasLimitOverride[][](0)); - - // No messages - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - - // 1 message missing - gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](1); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - - // 1 message in excess - gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](3); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - } - - function test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, new OffRamp.GasLimitOverride[][](0)); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, new OffRamp.GasLimitOverride[][](1)); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - - // 2nd report empty - gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](2); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - - // 1st report empty - gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](0); - gasLimitOverrides[1] = new OffRamp.GasLimitOverride[](1); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - - // 1st report oversized - gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](3); - - vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - } - - function test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - gasLimitOverrides[0][0].receiverExecutionGasLimit--; - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.InvalidManualExecutionGasLimit.selector, - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.messageId, - gasLimitOverrides[0][0].receiverExecutionGasLimit - ) - ); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - } - - function test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 1000; - Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](1); - messages[0] = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - // empty tokenGasOverride array provided - vm.expectRevert( - abi.encodeWithSelector(OffRamp.ManualExecutionGasAmountCountMismatch.selector, messages[0].header.messageId, 1) - ); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - - //trying with excesss elements tokenGasOverride array provided - gasLimitOverrides[0][0].tokenGasOverrides = new uint32[](3); - vm.expectRevert( - abi.encodeWithSelector(OffRamp.ManualExecutionGasAmountCountMismatch.selector, messages[0].header.messageId, 1) - ); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - } - - function test_manuallyExecute_InvalidTokenGasOverride_Revert() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 1000; - Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](1); - messages[0] = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - uint32[] memory tokenGasOverrides = new uint32[](2); - tokenGasOverrides[0] = DEFAULT_TOKEN_DEST_GAS_OVERHEAD; - tokenGasOverrides[1] = DEFAULT_TOKEN_DEST_GAS_OVERHEAD - 1; //invalid token gas override value - gasLimitOverrides[0][0].tokenGasOverrides = tokenGasOverrides; - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.InvalidManualExecutionTokenGasOverride.selector, - messages[0].header.messageId, - 1, - DEFAULT_TOKEN_DEST_GAS_OVERHEAD, - tokenGasOverrides[1] - ) - ); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - } - - function test_manuallyExecute_FailedTx_Revert() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - - messages[0].receiver = address(s_reverting_receiver); - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - s_offRamp.batchExecute( - _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) - ); - - s_reverting_receiver.setRevert(true); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.ExecutionError.selector, - messages[0].header.messageId, - abi.encodeWithSelector( - OffRamp.ReceiverError.selector, - abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, bytes("")) - ) - ) - ); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - } - - function test_manuallyExecute_ReentrancyFails_Success() public { - uint256 tokenAmount = 1e9; - IERC20 tokenToAbuse = IERC20(s_destFeeToken); - - // This needs to be deployed before the source chain message is sent - // because we need the address for the receiver. - ReentrancyAbuserMultiRamp receiver = new ReentrancyAbuserMultiRamp(address(s_destRouter), s_offRamp); - uint256 balancePre = tokenToAbuse.balanceOf(address(receiver)); - - // For this test any message will be flagged as correct by the - // commitStore. In a real scenario the abuser would have to actually - // 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 Internal.Any2EVMTokenTransfer[](1); - messages[0].tokenAmounts[0] = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[s_sourceFeeToken]), - destTokenAddress: s_destTokenBySourceToken[s_sourceFeeToken], - extraData: "", - amount: tokenAmount, - destGasAmount: MAX_TOKEN_POOL_RELEASE_OR_MINT_GAS - }); - - messages[0].receiver = address(receiver); - - messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); - - Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - // sets the report to be repeated on the ReentrancyAbuser to be able to replay - receiver.setPayload(report); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); - gasLimitOverrides[0][0].tokenGasOverrides = new uint32[](messages[0].tokenAmounts.length); - - // The first entry should be fine and triggers the second entry which is skipped. Due to the reentrancy - // the second completes first, so we expect the skip event before the success event. - vm.expectEmit(); - emit OffRamp.SkippedAlreadyExecutedMessage( - messages[0].header.sourceChainSelector, messages[0].header.sequenceNumber - ); - - vm.recordLogs(); - s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - // Since the tx failed we don't release the tokens - assertEq(tokenToAbuse.balanceOf(address(receiver)), balancePre + tokenAmount); - } - - function test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](3); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](2); - - for (uint64 i = 0; i < 3; ++i) { - messages1[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); - messages1[i].receiver = address(s_reverting_receiver); - messages1[i].header.messageId = _hashMessage(messages1[i], ON_RAMP_ADDRESS_1); - } - - for (uint64 i = 0; i < 2; ++i) { - messages2[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, i + 1); - messages2[i].receiver = address(s_reverting_receiver); - messages2[i].header.messageId = _hashMessage(messages2[i], ON_RAMP_ADDRESS_3); - } - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages1); - gasLimitOverrides[1] = _getGasLimitsFromMessages(messages2); - - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_3, true); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.CursedByRMN.selector, SOURCE_CHAIN_SELECTOR_3)); - - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - } - - function test_manuallyExecute_SourceChainSelectorMismatch_Revert() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](1); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); - - OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); - gasLimitOverrides[0] = _getGasLimitsFromMessages(messages1); - gasLimitOverrides[1] = _getGasLimitsFromMessages(messages2); - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.SourceChainSelectorMismatch.selector, SOURCE_CHAIN_SELECTOR_3, SOURCE_CHAIN_SELECTOR_1 - ) - ); - s_offRamp.manuallyExecute(reports, gasLimitOverrides); - } -} - -contract OffRamp_execute is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - } - - // Asserts that execute completes - function test_SingleReport_Success() public { - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted( - uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) - ); - - vm.recordLogs(); - - _execute(reports); - - assertExecutionStateChangedEventLogs( - SOURCE_CHAIN_SELECTOR_1, - messages[0].header.sequenceNumber, - messages[0].header.messageId, - _hashMessage(messages[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_MultipleReports_Success() public { - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted( - uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) - ); - - vm.recordLogs(); - _execute(reports); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - assertExecutionStateChangedEventLogs( - logs, - messages1[0].header.sourceChainSelector, - messages1[0].header.sequenceNumber, - messages1[0].header.messageId, - _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - messages1[1].header.sourceChainSelector, - messages1[1].header.sequenceNumber, - messages1[1].header.messageId, - _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - messages2[0].header.sourceChainSelector, - messages2[0].header.sequenceNumber, - messages2[0].header.messageId, - _hashMessage(messages2[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - - function test_LargeBatch_Success() public { - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](10); - for (uint64 i = 0; i < reports.length; ++i) { - Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](3); - messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1 + i * 3); - messages[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2 + i * 3); - messages[2] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3 + i * 3); - - reports[i] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - } - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted( - uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) - ); - - vm.recordLogs(); - _execute(reports); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - for (uint64 i = 0; i < reports.length; ++i) { - for (uint64 j = 0; j < reports[i].messages.length; ++j) { - assertExecutionStateChangedEventLogs( - logs, - reports[i].messages[j].header.sourceChainSelector, - reports[i].messages[j].header.sequenceNumber, - reports[i].messages[j].header.messageId, - _hashMessage(reports[i].messages[j], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - } - } - } - - function test_MultipleReportsWithPartialValidationFailures_Success() public { - _enableInboundMessageInterceptor(); - - Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); - Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); - - messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); - messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); - messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); - - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); - reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); - reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); - - s_inboundMessageInterceptor.setMessageIdValidationState(messages1[0].header.messageId, true); - s_inboundMessageInterceptor.setMessageIdValidationState(messages2[0].header.messageId, true); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted( - uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) - ); - - vm.recordLogs(); - _execute(reports); - - Vm.Log[] memory logs = vm.getRecordedLogs(); - - assertExecutionStateChangedEventLogs( - logs, - messages1[0].header.sourceChainSelector, - messages1[0].header.sequenceNumber, - messages1[0].header.messageId, - _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - IMessageInterceptor.MessageValidationError.selector, - abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) - ) - ); - - assertExecutionStateChangedEventLogs( - logs, - messages1[1].header.sourceChainSelector, - messages1[1].header.sequenceNumber, - messages1[1].header.messageId, - _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.SUCCESS, - "" - ); - - assertExecutionStateChangedEventLogs( - logs, - messages2[0].header.sourceChainSelector, - messages2[0].header.sequenceNumber, - messages2[0].header.messageId, - _hashMessage(messages2[0], ON_RAMP_ADDRESS_1), - Internal.MessageExecutionState.FAILURE, - abi.encodeWithSelector( - IMessageInterceptor.MessageValidationError.selector, - abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) - ) - ); - } - - // Reverts - - function test_UnauthorizedTransmitter_Revert() public { - bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; - - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); - s_offRamp.execute(reportContext, abi.encode(reports)); - } - - function test_NoConfig_Revert() public { - _redeployOffRampWithNoOCRConfigs(); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - bytes32[3] memory reportContext = [bytes32(""), s_configDigestExec, s_configDigestExec]; - - vm.startPrank(s_validTransmitters[0]); - vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); - s_offRamp.execute(reportContext, abi.encode(reports)); - } - - function test_NoConfigWithOtherConfigPresent_Revert() public { - _redeployOffRampWithNoOCRConfigs(); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - - MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); - ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ - ocrPluginType: uint8(Internal.OCRPluginType.Commit), - configDigest: s_configDigestCommit, - F: s_F, - isSignatureVerificationEnabled: true, - signers: s_validSigners, - transmitters: s_validTransmitters - }); - s_offRamp.setOCR3Configs(ocrConfigs); - - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - bytes32[3] memory reportContext = [bytes32(""), s_configDigestExec, s_configDigestExec]; - - vm.startPrank(s_validTransmitters[0]); - vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); - s_offRamp.execute(reportContext, abi.encode(reports)); - } - - function test_WrongConfigWithSigners_Revert() public { - _redeployOffRampWithNoOCRConfigs(); - s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); - - s_configDigestExec = _getBasicConfigDigest(1, s_validSigners, s_validTransmitters); - - MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); - ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ - ocrPluginType: uint8(Internal.OCRPluginType.Execution), - configDigest: s_configDigestExec, - F: s_F, - isSignatureVerificationEnabled: true, - signers: s_validSigners, - transmitters: s_validTransmitters - }); - - vm.expectRevert(OffRamp.SignatureVerificationNotAllowedInExecutionPlugin.selector); - s_offRamp.setOCR3Configs(ocrConfigs); - } - - function test_ZeroReports_Revert() public { - Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](0); - - vm.expectRevert(OffRamp.EmptyBatch.selector); - _execute(reports); - } - - function test_IncorrectArrayType_Revert() public { - bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; - - uint256[] memory wrongData = new uint256[](2); - wrongData[0] = 1; - - vm.startPrank(s_validTransmitters[0]); - vm.expectRevert(); - s_offRamp.execute(reportContext, abi.encode(wrongData)); - } - - function test_NonArray_Revert() public { - bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; - - Internal.Any2EVMRampMessage[] memory messages = - _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); - Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); - - vm.startPrank(s_validTransmitters[0]); - vm.expectRevert(); - s_offRamp.execute(reportContext, abi.encode(report)); - } -} - -contract OffRamp_getExecutionState is OffRampSetup { - mapping(uint64 sourceChainSelector => mapping(uint64 seqNum => Internal.MessageExecutionState state)) internal - s_differentialExecutionState; - - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 32 - function test_Fuzz_Differential_Success( - uint64 sourceChainSelector, - uint16[500] memory seqNums, - uint8[500] memory values - ) public { - for (uint256 i = 0; i < seqNums.length; ++i) { - // Only use the first three slots. This makes sure existing slots get overwritten - // as the tests uses 500 sequence numbers. - uint16 seqNum = seqNums[i] % 386; - Internal.MessageExecutionState state = Internal.MessageExecutionState(values[i] % 4); - s_differentialExecutionState[sourceChainSelector][seqNum] = state; - s_offRamp.setExecutionStateHelper(sourceChainSelector, seqNum, state); - assertEq(uint256(state), uint256(s_offRamp.getExecutionState(sourceChainSelector, seqNum))); - } - - for (uint256 i = 0; i < seqNums.length; ++i) { - uint16 seqNum = seqNums[i] % 386; - Internal.MessageExecutionState expectedState = s_differentialExecutionState[sourceChainSelector][seqNum]; - assertEq(uint256(expectedState), uint256(s_offRamp.getExecutionState(sourceChainSelector, seqNum))); - } - } - - function test_GetExecutionState_Success() public { - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 0, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 1, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (3 << 2)); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 1, Internal.MessageExecutionState.IN_PROGRESS); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2)); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 2, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2) + (3 << 4)); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 127, Internal.MessageExecutionState.IN_PROGRESS); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2) + (3 << 4) + (1 << 254)); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 128, Internal.MessageExecutionState.SUCCESS); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2) + (3 << 4) + (1 << 254)); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 1), 2); - - assertEq( - uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 0)) - ); - assertEq( - uint256(Internal.MessageExecutionState.IN_PROGRESS), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 1)) - ); - assertEq( - uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 2)) - ); - assertEq( - uint256(Internal.MessageExecutionState.IN_PROGRESS), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 127)) - ); - assertEq( - uint256(Internal.MessageExecutionState.SUCCESS), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 128)) - ); - } - - function test_GetDifferentChainExecutionState_Success() public { - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 0, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), 0); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 127, Internal.MessageExecutionState.IN_PROGRESS); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 254)); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), 0); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 128, Internal.MessageExecutionState.SUCCESS); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 254)); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 1), 2); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), 0); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 1), 0); - - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1 + 1, 127, Internal.MessageExecutionState.FAILURE); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 254)); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 1), 2); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), (3 << 254)); - assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 1), 0); - - assertEq( - uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 0)) - ); - assertEq( - uint256(Internal.MessageExecutionState.IN_PROGRESS), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 127)) - ); - assertEq( - uint256(Internal.MessageExecutionState.SUCCESS), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 128)) - ); - - assertEq( - uint256(Internal.MessageExecutionState.UNTOUCHED), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1 + 1, 0)) - ); - assertEq( - uint256(Internal.MessageExecutionState.FAILURE), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1 + 1, 127)) - ); - assertEq( - uint256(Internal.MessageExecutionState.UNTOUCHED), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1 + 1, 128)) - ); - } - - function test_FillExecutionState_Success() public { - for (uint64 i = 0; i < 384; ++i) { - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, i, Internal.MessageExecutionState.FAILURE); - } - - for (uint64 i = 0; i < 384; ++i) { - assertEq( - uint256(Internal.MessageExecutionState.FAILURE), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, i)) - ); - } - - for (uint64 i = 0; i < 3; ++i) { - assertEq(type(uint256).max, s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, i)); - } - - for (uint64 i = 0; i < 384; ++i) { - s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, i, Internal.MessageExecutionState.IN_PROGRESS); - } - - for (uint64 i = 0; i < 384; ++i) { - assertEq( - uint256(Internal.MessageExecutionState.IN_PROGRESS), - uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, i)) - ); - } - - for (uint64 i = 0; i < 3; ++i) { - // 0x555... == 0b101010101010..... - assertEq( - 0x5555555555555555555555555555555555555555555555555555555555555555, - s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, i) - ); - } - } -} - -contract OffRamp_trialExecute is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - } - - function test_trialExecute_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - IERC20 dstToken0 = IERC20(s_destTokens[0]); - uint256 startingBalance = dstToken0.balanceOf(message.receiver); - - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); - assertEq("", err); - - // Check that the tokens were transferred - assertEq(startingBalance + amounts[0], dstToken0.balanceOf(message.receiver)); - } - - function test_TokenHandlingErrorIsCaught_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - IERC20 dstToken0 = IERC20(s_destTokens[0]); - uint256 startingBalance = dstToken0.balanceOf(OWNER); - - bytes memory errorMessage = "Random token pool issue"; - - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - s_maybeRevertingPool.setShouldRevert(errorMessage); - - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); - assertEq(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, errorMessage), err); - - // Expect the balance to remain the same - assertEq(startingBalance, dstToken0.balanceOf(OWNER)); - } - - function test_RateLimitError_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 1000; - amounts[1] = 50; - - bytes memory errorMessage = abi.encodeWithSelector(RateLimiter.BucketOverfilled.selector); - - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - s_maybeRevertingPool.setShouldRevert(errorMessage); - - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); - assertEq(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, errorMessage), err); - } - - // TODO test actual pool exists but isn't compatible instead of just no pool - function test_TokenPoolIsNotAContract_Success() public { - uint256[] memory amounts = new uint256[](2); - amounts[0] = 10000; - Internal.Any2EVMRampMessage memory message = - _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); - - // Happy path, pool is correct - (Internal.MessageExecutionState newState, bytes memory err) = - s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - - assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); - assertEq("", err); - - // address 0 has no contract - assertEq(address(0).code.length, 0); - - message.tokenAmounts[0] = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(address(0)), - destTokenAddress: address(0), - extraData: "", - amount: message.tokenAmounts[0].amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - message.header.messageId = _hashMessage(message, ON_RAMP_ADDRESS_1); - - // Unhappy path, no revert but marked as failed. - (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); - assertEq(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, address(0)), err); - - address notAContract = makeAddr("not_a_contract"); - - message.tokenAmounts[0] = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(address(0)), - destTokenAddress: notAContract, - extraData: "", - amount: message.tokenAmounts[0].amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - message.header.messageId = _hashMessage(message, ON_RAMP_ADDRESS_1); - - (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); - - assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); - assertEq(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, address(0)), err); - } -} - -contract OffRamp_releaseOrMintSingleToken is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - } - - function test__releaseOrMintSingleToken_Success() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - bytes memory originalSender = abi.encode(OWNER); - bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); - - IERC20 dstToken1 = IERC20(s_destTokenBySourceToken[token]); - uint256 startingBalance = dstToken1.balanceOf(OWNER); - - Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: s_destTokenBySourceToken[token], - extraData: "", - amount: amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - vm.expectCall( - s_destPoolBySourceToken[token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: originalSender, - receiver: OWNER, - amount: amount, - localToken: s_destTokenBySourceToken[token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: tokenAmount.sourcePoolAddress, - sourcePoolData: tokenAmount.extraData, - offchainTokenData: offchainTokenData - }) - ) - ); - - s_offRamp.releaseOrMintSingleToken(tokenAmount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData); - - assertEq(startingBalance + amount, dstToken1.balanceOf(OWNER)); - } - - function test_releaseOrMintToken_InvalidDataLength_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - - Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: s_destTokenBySourceToken[token], - extraData: "", - amount: amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - // Mock the call so returns 2 slots of data - vm.mockCall( - s_destTokenBySourceToken[token], abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), abi.encode(0, 0) - ); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.InvalidDataLength.selector, Internal.MAX_BALANCE_OF_RET_BYTES, 64)); - - s_offRamp.releaseOrMintSingleToken(tokenAmount, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR, ""); - } - - function test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - - Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: s_destTokenBySourceToken[token], - extraData: "", - amount: amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - bytes memory revertData = "failed to balanceOf"; - - // Mock the call so returns 2 slots of data - vm.mockCallRevert( - s_destTokenBySourceToken[token], abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), revertData - ); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, revertData)); - - s_offRamp.releaseOrMintSingleToken(tokenAmount, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR, ""); - } - - function test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - uint256 mockedStaticBalance = 50000; - - Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: s_destTokenBySourceToken[token], - extraData: "", - amount: amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - vm.mockCall( - s_destTokenBySourceToken[token], - abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), - abi.encode(mockedStaticBalance) - ); - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.ReleaseOrMintBalanceMismatch.selector, amount, mockedStaticBalance, mockedStaticBalance - ) - ); - - s_offRamp.releaseOrMintSingleToken(tokenAmount, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR, ""); - } - - function test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - uint256 mockedStaticBalance = 50000; - - Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: s_destTokenBySourceToken[token], - extraData: "", - amount: amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - // This should make the call fail if it does not skip the check - vm.mockCall( - s_destTokenBySourceToken[token], - abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), - abi.encode(mockedStaticBalance) - ); - - s_offRamp.releaseOrMintSingleToken( - tokenAmount, abi.encode(OWNER), s_destPoolBySourceToken[token], SOURCE_CHAIN_SELECTOR, "" - ); - } - - function test__releaseOrMintSingleToken_NotACompatiblePool_Revert() public { - uint256 amount = 123123; - address token = s_sourceTokens[0]; - address destToken = s_destTokenBySourceToken[token]; - vm.label(destToken, "destToken"); - bytes memory originalSender = abi.encode(OWNER); - bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); - - Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: destToken, - extraData: "", - amount: amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - // Address(0) should always revert - address returnedPool = address(0); - - vm.mockCall( - address(s_tokenAdminRegistry), - abi.encodeWithSelector(ITokenAdminRegistry.getPool.selector, destToken), - abi.encode(returnedPool) - ); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, returnedPool)); - - 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); - - vm.mockCall( - address(s_tokenAdminRegistry), - abi.encodeWithSelector(ITokenAdminRegistry.getPool.selector, destToken), - abi.encode(returnedPool) - ); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, returnedPool)); - - s_offRamp.releaseOrMintSingleToken(tokenAmount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData); - } - - function test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() public { - address receiver = makeAddr("receiver"); - uint256 amount = 123123; - address token = s_sourceTokens[0]; - address destToken = s_destTokenBySourceToken[token]; - bytes memory originalSender = abi.encode(OWNER); - bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); - - Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), - destTokenAddress: destToken, - extraData: "", - amount: amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - bytes memory revertData = "call reverted :o"; - - vm.mockCallRevert(destToken, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount), revertData); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, revertData)); - s_offRamp.releaseOrMintSingleToken( - tokenAmount, originalSender, receiver, SOURCE_CHAIN_SELECTOR_1, offchainTokenData - ); - } -} - -contract OffRamp_releaseOrMintTokens is OffRampSetup { - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - } - - function test_releaseOrMintTokens_Success() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - IERC20 dstToken1 = IERC20(s_destFeeToken); - uint256 startingBalance = dstToken1.balanceOf(OWNER); - uint256 amount1 = 100; - srcTokenAmounts[0].amount = amount1; - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - offchainTokenData[0] = abi.encode(0x12345678); - - Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); - - vm.expectCall( - s_destPoolBySourceToken[srcTokenAmounts[0].token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: OWNER, - amount: srcTokenAmounts[0].amount, - localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, - sourcePoolData: sourceTokenAmounts[0].extraData, - offchainTokenData: offchainTokenData[0] - }) - ) - ); - - s_offRamp.releaseOrMintTokens( - sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, new uint32[](0) - ); - - assertEq(startingBalance + amount1, dstToken1.balanceOf(OWNER)); - } - - function test_releaseOrMintTokens_WithGasOverride_Success() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - IERC20 dstToken1 = IERC20(s_destFeeToken); - uint256 startingBalance = dstToken1.balanceOf(OWNER); - uint256 amount1 = 100; - srcTokenAmounts[0].amount = amount1; - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - offchainTokenData[0] = abi.encode(0x12345678); - - Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); - - vm.expectCall( - s_destPoolBySourceToken[srcTokenAmounts[0].token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: OWNER, - amount: srcTokenAmounts[0].amount, - localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, - sourcePoolData: sourceTokenAmounts[0].extraData, - offchainTokenData: offchainTokenData[0] - }) - ) - ); - - uint32[] memory gasOverrides = new uint32[](sourceTokenAmounts.length); - for (uint256 i = 0; i < gasOverrides.length; i++) { - gasOverrides[i] = DEFAULT_TOKEN_DEST_GAS_OVERHEAD + 1; - } - s_offRamp.releaseOrMintTokens( - sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, gasOverrides - ); - - assertEq(startingBalance + amount1, dstToken1.balanceOf(OWNER)); - } - - function test_releaseOrMintTokens_destDenominatedDecimals_Success() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - uint256 amount = 100; - uint256 destinationDenominationMultiplier = 1000; - srcTokenAmounts[1].amount = amount; - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - - Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); - - address pool = s_destPoolBySourceToken[srcTokenAmounts[1].token]; - address destToken = s_destTokenBySourceToken[srcTokenAmounts[1].token]; - - MaybeRevertingBurnMintTokenPool(pool).setReleaseOrMintMultiplier(destinationDenominationMultiplier); - - Client.EVMTokenAmount[] memory destTokenAmounts = s_offRamp.releaseOrMintTokens( - sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, new uint32[](0) - ); - assertEq(destTokenAmounts[1].amount, amount * destinationDenominationMultiplier); - assertEq(destTokenAmounts[1].token, destToken); - } - - // Revert - - function test_TokenHandlingError_Reverts() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - - bytes memory unknownError = bytes("unknown error"); - s_maybeRevertingPool.setShouldRevert(unknownError); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, unknownError)); - - s_offRamp.releaseOrMintTokens( - _getDefaultSourceTokenData(srcTokenAmounts), - abi.encode(OWNER), - OWNER, - SOURCE_CHAIN_SELECTOR_1, - new bytes[](srcTokenAmounts.length), - new uint32[](0) - ); - } - - function test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() public { - uint256 amount = 100; - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - srcTokenAmounts[0].amount = amount; - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); - - vm.mockCall( - s_destPoolBySourceToken[srcTokenAmounts[0].token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: OWNER, - amount: amount, - localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, - sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, - sourcePoolData: sourceTokenAmounts[0].extraData, - offchainTokenData: offchainTokenData[0] - }) - ), - // Includes the amount twice, this will revert due to the return data being to long - abi.encode(amount, amount) - ); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.InvalidDataLength.selector, Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, 64)); - - s_offRamp.releaseOrMintTokens( - sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, new uint32[](0) - ); - } - - function test__releaseOrMintTokens_PoolIsNotAPool_Reverts() public { - // The offRamp is a contract, but not a pool - address fakePoolAddress = address(s_offRamp); - - Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = new Internal.Any2EVMTokenTransfer[](1); - sourceTokenAmounts[0] = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(fakePoolAddress), - destTokenAddress: address(s_offRamp), - extraData: "", - amount: 1, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, address(0))); - s_offRamp.releaseOrMintTokens( - sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, new bytes[](1), new uint32[](0) - ); - } - - function test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() public { - Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - uint256 amount1 = 100; - srcTokenAmounts[0].amount = amount1; - - bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); - offchainTokenData[0] = abi.encode(0x12345678); - - Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); - - vm.expectCall( - s_destPoolBySourceToken[srcTokenAmounts[0].token], - abi.encodeWithSelector( - LockReleaseTokenPool.releaseOrMint.selector, - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: OWNER, - amount: srcTokenAmounts[0].amount, - localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], - remoteChainSelector: SOURCE_CHAIN_SELECTOR_3, - sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, - sourcePoolData: sourceTokenAmounts[0].extraData, - offchainTokenData: offchainTokenData[0] - }) - ) - ); - vm.expectRevert(); - s_offRamp.releaseOrMintTokens( - sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_3, offchainTokenData, new uint32[](0) - ); - } - - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 1024 - // Uint256 gives a good range of values to test, both inside and outside of the eth address space. - function test_Fuzz__releaseOrMintTokens_AnyRevertIsCaught_Success( - address destPool - ) public { - // Input 447301751254033913445893214690834296930546521452, which is 0x4E59B44847B379578588920CA78FBF26C0B4956C - // triggers some Create2Deployer and causes it to fail - vm.assume(destPool != 0x4e59b44847b379578588920cA78FbF26c0B4956C); - bytes memory unusedVar = abi.encode(makeAddr("unused")); - Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = new Internal.Any2EVMTokenTransfer[](1); - sourceTokenAmounts[0] = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: unusedVar, - destTokenAddress: destPool, - extraData: unusedVar, - amount: 1, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - - try s_offRamp.releaseOrMintTokens( - sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, new bytes[](1), new uint32[](0) - ) {} catch (bytes memory reason) { - // Any revert should be a TokenHandlingError, InvalidEVMAddress, InvalidDataLength or NoContract as those are caught by the offramp - assertTrue( - bytes4(reason) == OffRamp.TokenHandlingError.selector || bytes4(reason) == Internal.InvalidEVMAddress.selector - || bytes4(reason) == OffRamp.InvalidDataLength.selector - || bytes4(reason) == CallWithExactGas.NoContract.selector - || bytes4(reason) == OffRamp.NotACompatiblePool.selector, - "Expected TokenHandlingError or InvalidEVMAddress" - ); - - if (uint160(destPool) > type(uint160).max) { - assertEq(reason, abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, abi.encode(destPool))); - } - } - } -} - -contract OffRamp_applySourceChainConfigUpdates is OffRampSetup { - function test_ApplyZeroUpdates_Success() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); - - vm.recordLogs(); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - // No logs emitted - Vm.Log[] memory logEntries = vm.getRecordedLogs(); - assertEq(logEntries.length, 0); - - assertEq(s_offRamp.getSourceChainSelectors().length, 0); - } - - function test_AddNewChain_Success() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - OffRamp.SourceChainConfig memory expectedSourceChainConfig = - OffRamp.SourceChainConfig({router: s_destRouter, isEnabled: true, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_1}); - - vm.expectEmit(); - emit OffRamp.SourceChainSelectorAdded(SOURCE_CHAIN_SELECTOR_1); - - vm.expectEmit(); - emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1, expectedSourceChainConfig); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - _assertSourceChainConfigEquality(s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR_1), expectedSourceChainConfig); - } - - function test_ReplaceExistingChain_Success() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - sourceChainConfigs[0].isEnabled = false; - OffRamp.SourceChainConfig memory expectedSourceChainConfig = - OffRamp.SourceChainConfig({router: s_destRouter, isEnabled: false, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_1}); - - vm.expectEmit(); - emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1, expectedSourceChainConfig); - - vm.recordLogs(); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - // No log emitted for chain selector added (only for setting the config) - Vm.Log[] memory logEntries = vm.getRecordedLogs(); - assertEq(logEntries.length, 1); - - _assertSourceChainConfigEquality(s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR_1), expectedSourceChainConfig); - - uint256[] memory resultSourceChainSelectors = s_offRamp.getSourceChainSelectors(); - assertEq(resultSourceChainSelectors.length, 1); - } - - function test_AddMultipleChains_Success() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](3); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: abi.encode(ON_RAMP_ADDRESS_1, 0), - isEnabled: true - }); - sourceChainConfigs[1] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 1, - onRamp: abi.encode(ON_RAMP_ADDRESS_1, 1), - isEnabled: false - }); - sourceChainConfigs[2] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 2, - onRamp: abi.encode(ON_RAMP_ADDRESS_1, 2), - isEnabled: true - }); - - OffRamp.SourceChainConfig[] memory expectedSourceChainConfigs = new OffRamp.SourceChainConfig[](3); - for (uint256 i = 0; i < 3; ++i) { - expectedSourceChainConfigs[i] = OffRamp.SourceChainConfig({ - router: s_destRouter, - isEnabled: sourceChainConfigs[i].isEnabled, - minSeqNr: 1, - onRamp: abi.encode(ON_RAMP_ADDRESS_1, i) - }); - - vm.expectEmit(); - emit OffRamp.SourceChainSelectorAdded(sourceChainConfigs[i].sourceChainSelector); - - vm.expectEmit(); - emit OffRamp.SourceChainConfigSet(sourceChainConfigs[i].sourceChainSelector, expectedSourceChainConfigs[i]); - } - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - for (uint256 i = 0; i < 3; ++i) { - _assertSourceChainConfigEquality( - s_offRamp.getSourceChainConfig(sourceChainConfigs[i].sourceChainSelector), expectedSourceChainConfigs[i] - ); - } - } - - // Setting lower fuzz run as 256 runs was sometimes resulting in flakes. - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 32 - function test_Fuzz_applySourceChainConfigUpdate_Success( - OffRamp.SourceChainConfigArgs memory sourceChainConfigArgs - ) public { - // Skip invalid inputs - vm.assume(sourceChainConfigArgs.sourceChainSelector != 0); - vm.assume(sourceChainConfigArgs.onRamp.length != 0); - vm.assume(address(sourceChainConfigArgs.router) != address(0)); - - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](2); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - sourceChainConfigs[1] = sourceChainConfigArgs; - - // Handle cases when an update occurs - bool isNewChain = sourceChainConfigs[1].sourceChainSelector != SOURCE_CHAIN_SELECTOR_1; - if (!isNewChain) { - sourceChainConfigs[1].onRamp = sourceChainConfigs[0].onRamp; - } - - OffRamp.SourceChainConfig memory expectedSourceChainConfig = OffRamp.SourceChainConfig({ - router: sourceChainConfigArgs.router, - isEnabled: sourceChainConfigArgs.isEnabled, - minSeqNr: 1, - onRamp: sourceChainConfigArgs.onRamp - }); - - if (isNewChain) { - vm.expectEmit(); - emit OffRamp.SourceChainSelectorAdded(sourceChainConfigArgs.sourceChainSelector); - } - - vm.expectEmit(); - emit OffRamp.SourceChainConfigSet(sourceChainConfigArgs.sourceChainSelector, expectedSourceChainConfig); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - _assertSourceChainConfigEquality( - s_offRamp.getSourceChainConfig(sourceChainConfigArgs.sourceChainSelector), expectedSourceChainConfig - ); - } - - function test_ReplaceExistingChainOnRamp_Success() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - sourceChainConfigs[0].onRamp = ON_RAMP_ADDRESS_2; - - vm.expectEmit(); - emit OffRamp.SourceChainConfigSet( - SOURCE_CHAIN_SELECTOR_1, - OffRamp.SourceChainConfig({router: s_destRouter, isEnabled: true, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_2}) - ); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } - - function test_allowNonOnRampUpdateAfterLaneIsUsed_success() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: 2, - merkleRoot: "test #2" - }); - - _commit( - OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }), - s_latestSequenceNumber - ); - - vm.startPrank(OWNER); - - // Allow changes to the Router even after the seqNum is not 1 - assertGt(s_offRamp.getSourceChainConfig(sourceChainConfigs[0].sourceChainSelector).minSeqNr, 1); - - sourceChainConfigs[0].router = IRouter(makeAddr("newRouter")); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } - - // Reverts - - function test_ZeroOnRampAddress_Revert() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: new bytes(0), - isEnabled: true - }); - - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - sourceChainConfigs[0].onRamp = abi.encode(address(0)); - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } - - function test_RouterAddress_Revert() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: IRouter(address(0)), - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } - - function test_ZeroSourceChainSelector_Revert() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: 0, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - vm.expectRevert(OffRamp.ZeroChainSelectorNotAllowed.selector); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } - - function test_InvalidOnRampUpdate_Revert() public { - OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); - sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ - router: s_destRouter, - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRamp: ON_RAMP_ADDRESS_1, - isEnabled: true - }); - - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: 2, - merkleRoot: "test #2" - }); - - _commit( - OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }), - s_latestSequenceNumber - ); - - vm.stopPrank(); - vm.startPrank(OWNER); - - sourceChainConfigs[0].onRamp = ON_RAMP_ADDRESS_2; - - vm.expectRevert(abi.encodeWithSelector(OffRamp.InvalidOnRampUpdate.selector, SOURCE_CHAIN_SELECTOR_1)); - s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); - } -} - -contract OffRamp_commit is OffRampSetup { - uint64 internal s_maxInterval = 12; - - function setUp() public virtual override { - super.setUp(); - _setupMultipleOffRamps(); - - s_latestSequenceNumber = uint64(uint256(s_configDigestCommit)); - } - - function test_ReportAndPriceUpdate_Success() public { - OffRamp.CommitReport memory commitReport = _constructCommitReport(); - - vm.expectEmit(); - emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - - assertEq(s_maxInterval + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); - assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); - } - - function test_ReportOnlyRootSuccess_gas() public { - uint64 max1 = 931; - bytes32 root = "Only a single root"; - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: max1, - merkleRoot: root - }); - - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectEmit(); - emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - - assertEq(max1 + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); - assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); - assertEq(block.timestamp, s_offRamp.getMerkleRoot(SOURCE_CHAIN_SELECTOR_1, root)); - } - - function test_RootWithRMNDisabled_success() public { - // force RMN verification to fail - vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); - - // but ☝️ doesn't matter because RMN verification is disabled - OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); - dynamicConfig.isRMNVerificationDisabled = true; - s_offRamp.setDynamicConfig(dynamicConfig); - - uint64 max1 = 931; - bytes32 root = "Only a single root"; - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: max1, - merkleRoot: root - }); - - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectEmit(); - emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - - assertEq(max1 + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); - assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); - assertEq(block.timestamp, s_offRamp.getMerkleRoot(SOURCE_CHAIN_SELECTOR_1, root)); - } - - function test_StaleReportWithRoot_Success() public { - uint64 maxSeq = 12; - uint224 tokenStartPrice = IFeeQuoter(s_offRamp.getDynamicConfig().feeQuoter).getTokenPrice(s_sourceFeeToken).value; - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: maxSeq, - merkleRoot: "stale report 1" - }); - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectEmit(); - emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - - assertEq(maxSeq + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); - assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); - - commitReport.merkleRoots[0].minSeqNr = maxSeq + 1; - commitReport.merkleRoots[0].maxSeqNr = maxSeq * 2; - commitReport.merkleRoots[0].merkleRoot = "stale report 2"; - - vm.expectEmit(); - emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - - assertEq(maxSeq * 2 + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); - assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); - assertEq(tokenStartPrice, IFeeQuoter(s_offRamp.getDynamicConfig().feeQuoter).getTokenPrice(s_sourceFeeToken).value); - } - - function test_OnlyTokenPriceUpdates_Success() public { - // force RMN verification to fail - vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); - OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - - assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); - } - - function test_OnlyGasPriceUpdates_Success() public { - // force RMN verification to fail - vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); - - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); - OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); - } - - function test_PriceSequenceNumberCleared_Success() public { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); - OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - _commit(commitReport, s_latestSequenceNumber); - - assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); - - vm.startPrank(OWNER); - MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); - ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ - ocrPluginType: uint8(Internal.OCRPluginType.Execution), - configDigest: s_configDigestExec, - F: s_F, - isSignatureVerificationEnabled: false, - signers: s_emptySigners, - transmitters: s_validTransmitters - }); - s_offRamp.setOCR3Configs(ocrConfigs); - - // Execution plugin OCR config should not clear latest epoch and round - assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); - - // Commit plugin config should clear latest epoch & round - ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ - ocrPluginType: uint8(Internal.OCRPluginType.Commit), - configDigest: s_configDigestCommit, - F: s_F, - isSignatureVerificationEnabled: true, - signers: s_validSigners, - transmitters: s_validTransmitters - }); - s_offRamp.setOCR3Configs(ocrConfigs); - - assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); - - // The same sequence number can be reported again - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - - _commit(commitReport, s_latestSequenceNumber); - } - - function test_ValidPriceUpdateThenStaleReportWithRoot_Success() public { - uint64 maxSeq = 12; - uint224 tokenPrice1 = 4e18; - uint224 tokenPrice2 = 5e18; - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); - OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, tokenPrice1), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, tokenPrice1, block.timestamp); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); - - roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: maxSeq, - merkleRoot: "stale report" - }); - commitReport.priceUpdates = _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, tokenPrice2); - commitReport.merkleRoots = roots; - - vm.expectEmit(); - emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); - - vm.expectEmit(); - emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); - - _commit(commitReport, s_latestSequenceNumber); - - assertEq(maxSeq + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); - assertEq(tokenPrice1, IFeeQuoter(s_offRamp.getDynamicConfig().feeQuoter).getTokenPrice(s_sourceFeeToken).value); - assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); - } - - // Reverts - - function test_UnauthorizedTransmitter_Revert() public { - OffRamp.CommitReport memory commitReport = _constructCommitReport(); - - bytes32[3] memory reportContext = - [s_configDigestCommit, bytes32(uint256(s_latestSequenceNumber)), s_configDigestCommit]; - - (bytes32[] memory rs, bytes32[] memory ss,, bytes32 rawVs) = - _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, s_F + 1); - - vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); - s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); - } - - function test_NoConfig_Revert() public { - _redeployOffRampWithNoOCRConfigs(); - - OffRamp.CommitReport memory commitReport = _constructCommitReport(); - - bytes32[3] memory reportContext = [bytes32(""), s_configDigestCommit, s_configDigestCommit]; - (bytes32[] memory rs, bytes32[] memory ss,, bytes32 rawVs) = - _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, s_F + 1); - - vm.startPrank(s_validTransmitters[0]); - vm.expectRevert(); - s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); - } - - function test_NoConfigWithOtherConfigPresent_Revert() public { - _redeployOffRampWithNoOCRConfigs(); - - MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); - ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ - ocrPluginType: uint8(Internal.OCRPluginType.Execution), - configDigest: s_configDigestExec, - F: s_F, - isSignatureVerificationEnabled: false, - signers: s_emptySigners, - transmitters: s_validTransmitters - }); - s_offRamp.setOCR3Configs(ocrConfigs); - - OffRamp.CommitReport memory commitReport = _constructCommitReport(); - - bytes32[3] memory reportContext = [bytes32(""), s_configDigestCommit, s_configDigestCommit]; - (bytes32[] memory rs, bytes32[] memory ss,, bytes32 rawVs) = - _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, s_F + 1); - - vm.startPrank(s_validTransmitters[0]); - vm.expectRevert(); - s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); - } - - function test_FailedRMNVerification_Reverts() public { - // force RMN verification to fail - vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); - - OffRamp.CommitReport memory commitReport = _constructCommitReport(); - vm.expectRevert(); - _commit(commitReport, s_latestSequenceNumber); - } - - function test_Unhealthy_Revert() public { - _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - minSeqNr: 1, - maxSeqNr: 2, - merkleRoot: "Only a single root", - onRampAddress: abi.encode(ON_RAMP_ADDRESS_1) - }); - - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.CursedByRMN.selector, roots[0].sourceChainSelector)); - _commit(commitReport, s_latestSequenceNumber); - } - - function test_InvalidRootRevert() public { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: 4, - merkleRoot: bytes32(0) - }); - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectRevert(OffRamp.InvalidRoot.selector); - _commit(commitReport, s_latestSequenceNumber); - } - - function test_InvalidInterval_Revert() public { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 2, - maxSeqNr: 2, - merkleRoot: bytes32(0) - }); - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.InvalidInterval.selector, roots[0].sourceChainSelector, roots[0].minSeqNr, roots[0].maxSeqNr - ) - ); - _commit(commitReport, s_latestSequenceNumber); - } - - function test_InvalidIntervalMinLargerThanMax_Revert() public { - s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR); - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: 0, - merkleRoot: bytes32(0) - }); - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectRevert( - abi.encodeWithSelector( - OffRamp.InvalidInterval.selector, roots[0].sourceChainSelector, roots[0].minSeqNr, roots[0].maxSeqNr - ) - ); - _commit(commitReport, s_latestSequenceNumber); - } - - function test_ZeroEpochAndRound_Revert() public { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); - OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - - vm.expectRevert(OffRamp.StaleCommitReport.selector); - _commit(commitReport, 0); - } - - function test_OnlyPriceUpdateStaleReport_Revert() public { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); - OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - - vm.expectEmit(); - emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); - _commit(commitReport, s_latestSequenceNumber); - - vm.expectRevert(OffRamp.StaleCommitReport.selector); - _commit(commitReport, s_latestSequenceNumber); - } - - function test_SourceChainNotEnabled_Revert() public { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: 0, - onRampAddress: abi.encode(ON_RAMP_ADDRESS_1), - minSeqNr: 1, - maxSeqNr: 2, - merkleRoot: "Only a single root" - }); - - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - vm.expectRevert(abi.encodeWithSelector(OffRamp.SourceChainNotEnabled.selector, 0)); - _commit(commitReport, s_latestSequenceNumber); - } - - function test_RootAlreadyCommitted_Revert() public { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: 2, - merkleRoot: "Only a single root" - }); - OffRamp.CommitReport memory commitReport = - OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); - - _commit(commitReport, s_latestSequenceNumber); - commitReport.merkleRoots[0].minSeqNr = 3; - commitReport.merkleRoots[0].maxSeqNr = 3; - - vm.expectRevert( - abi.encodeWithSelector(OffRamp.RootAlreadyCommitted.selector, roots[0].sourceChainSelector, roots[0].merkleRoot) - ); - _commit(commitReport, ++s_latestSequenceNumber); - } - - function test_CommitOnRampMismatch_Revert() public { - OffRamp.CommitReport memory commitReport = _constructCommitReport(); - - commitReport.merkleRoots[0].onRampAddress = ON_RAMP_ADDRESS_2; - - vm.expectRevert(abi.encodeWithSelector(OffRamp.CommitOnRampMismatch.selector, ON_RAMP_ADDRESS_2, ON_RAMP_ADDRESS_1)); - _commit(commitReport, s_latestSequenceNumber); - } - - function _constructCommitReport() internal view returns (OffRamp.CommitReport memory) { - Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); - roots[0] = Internal.MerkleRoot({ - sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, - onRampAddress: ON_RAMP_ADDRESS_1, - minSeqNr: 1, - maxSeqNr: s_maxInterval, - merkleRoot: "test #2" - }); - - return OffRamp.CommitReport({ - priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), - merkleRoots: roots, - rmnSignatures: s_rmnSignatures - }); - } -} - -contract OffRamp_afterOC3ConfigSet is OffRampSetup { - function test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() public { - s_offRamp = new OffRampHelper( - OffRamp.StaticConfig({ - chainSelector: DEST_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - tokenAdminRegistry: address(s_tokenAdminRegistry), - nonceManager: address(s_inboundNonceManager) - }), - _generateDynamicOffRampConfig(address(s_feeQuoter)), - new OffRamp.SourceChainConfigArgs[](0) - ); - - MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); - ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ - ocrPluginType: uint8(Internal.OCRPluginType.Commit), - configDigest: s_configDigestCommit, - F: s_F, - isSignatureVerificationEnabled: false, - signers: s_validSigners, - transmitters: s_validTransmitters - }); - - vm.expectRevert(OffRamp.SignatureVerificationRequiredInCommitPlugin.selector); - s_offRamp.setOCR3Configs(ocrConfigs); - } -} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.afterOC3ConfigSet.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.afterOC3ConfigSet.t.sol new file mode 100644 index 00000000000..91694dbcb05 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.afterOC3ConfigSet.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../../libraries/Internal.sol"; +import {MultiOCR3Base} from "../../../ocr/MultiOCR3Base.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampHelper} from "../../helpers/OffRampHelper.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +contract OffRamp_afterOC3ConfigSet is OffRampSetup { + function test_afterOCR3ConfigSet_SignatureVerificationDisabled_Revert() public { + s_offRamp = new OffRampHelper( + OffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) + }), + _generateDynamicOffRampConfig(address(s_feeQuoter)), + new OffRamp.SourceChainConfigArgs[](0) + ); + + MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); + ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ + ocrPluginType: uint8(Internal.OCRPluginType.Commit), + configDigest: s_configDigestCommit, + F: F, + isSignatureVerificationEnabled: false, + signers: s_validSigners, + transmitters: s_validTransmitters + }); + + vm.expectRevert(OffRamp.SignatureVerificationRequiredInCommitPlugin.selector); + s_offRamp.setOCR3Configs(ocrConfigs); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.applySourceChainConfigUpdates.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.applySourceChainConfigUpdates.t.sol new file mode 100644 index 00000000000..7ed5c22b800 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.applySourceChainConfigUpdates.t.sol @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IRouter} from "../../../interfaces/IRouter.sol"; + +import {Internal} from "../../../libraries/Internal.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {Vm} from "forge-std/Vm.sol"; + +contract OffRamp_applySourceChainConfigUpdates is OffRampSetup { + function test_ApplyZeroUpdates_Success() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); + + vm.recordLogs(); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + // No logs emitted + Vm.Log[] memory logEntries = vm.getRecordedLogs(); + assertEq(logEntries.length, 0); + + assertEq(s_offRamp.getSourceChainSelectors().length, 0); + } + + function test_AddNewChain_Success() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + OffRamp.SourceChainConfig memory expectedSourceChainConfig = + OffRamp.SourceChainConfig({router: s_destRouter, isEnabled: true, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_1}); + + vm.expectEmit(); + emit OffRamp.SourceChainSelectorAdded(SOURCE_CHAIN_SELECTOR_1); + + vm.expectEmit(); + emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1, expectedSourceChainConfig); + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + _assertSourceChainConfigEquality(s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR_1), expectedSourceChainConfig); + } + + function test_ReplaceExistingChain_Success() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + sourceChainConfigs[0].isEnabled = false; + OffRamp.SourceChainConfig memory expectedSourceChainConfig = + OffRamp.SourceChainConfig({router: s_destRouter, isEnabled: false, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_1}); + + vm.expectEmit(); + emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1, expectedSourceChainConfig); + + vm.recordLogs(); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + // No log emitted for chain selector added (only for setting the config) + Vm.Log[] memory logEntries = vm.getRecordedLogs(); + assertEq(logEntries.length, 1); + + _assertSourceChainConfigEquality(s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR_1), expectedSourceChainConfig); + + uint256[] memory resultSourceChainSelectors = s_offRamp.getSourceChainSelectors(); + assertEq(resultSourceChainSelectors.length, 1); + } + + function test_AddMultipleChains_Success() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](3); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: abi.encode(ON_RAMP_ADDRESS_1, 0), + isEnabled: true + }); + sourceChainConfigs[1] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 1, + onRamp: abi.encode(ON_RAMP_ADDRESS_1, 1), + isEnabled: false + }); + sourceChainConfigs[2] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 2, + onRamp: abi.encode(ON_RAMP_ADDRESS_1, 2), + isEnabled: true + }); + + OffRamp.SourceChainConfig[] memory expectedSourceChainConfigs = new OffRamp.SourceChainConfig[](3); + for (uint256 i = 0; i < 3; ++i) { + expectedSourceChainConfigs[i] = OffRamp.SourceChainConfig({ + router: s_destRouter, + isEnabled: sourceChainConfigs[i].isEnabled, + minSeqNr: 1, + onRamp: abi.encode(ON_RAMP_ADDRESS_1, i) + }); + + vm.expectEmit(); + emit OffRamp.SourceChainSelectorAdded(sourceChainConfigs[i].sourceChainSelector); + + vm.expectEmit(); + emit OffRamp.SourceChainConfigSet(sourceChainConfigs[i].sourceChainSelector, expectedSourceChainConfigs[i]); + } + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + for (uint256 i = 0; i < 3; ++i) { + _assertSourceChainConfigEquality( + s_offRamp.getSourceChainConfig(sourceChainConfigs[i].sourceChainSelector), expectedSourceChainConfigs[i] + ); + } + } + + // Setting lower fuzz run as 256 runs was sometimes resulting in flakes. + /// forge-config: default.fuzz.runs = 32 + /// forge-config: ccip.fuzz.runs = 32 + function test_Fuzz_applySourceChainConfigUpdate_Success( + OffRamp.SourceChainConfigArgs memory sourceChainConfigArgs + ) public { + // Skip invalid inputs + vm.assume(sourceChainConfigArgs.sourceChainSelector != 0); + vm.assume(sourceChainConfigArgs.onRamp.length != 0); + vm.assume(address(sourceChainConfigArgs.router) != address(0)); + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](2); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + sourceChainConfigs[1] = sourceChainConfigArgs; + + // Handle cases when an update occurs + bool isNewChain = sourceChainConfigs[1].sourceChainSelector != SOURCE_CHAIN_SELECTOR_1; + if (!isNewChain) { + sourceChainConfigs[1].onRamp = sourceChainConfigs[0].onRamp; + } + + OffRamp.SourceChainConfig memory expectedSourceChainConfig = OffRamp.SourceChainConfig({ + router: sourceChainConfigArgs.router, + isEnabled: sourceChainConfigArgs.isEnabled, + minSeqNr: 1, + onRamp: sourceChainConfigArgs.onRamp + }); + + if (isNewChain) { + vm.expectEmit(); + emit OffRamp.SourceChainSelectorAdded(sourceChainConfigArgs.sourceChainSelector); + } + + vm.expectEmit(); + emit OffRamp.SourceChainConfigSet(sourceChainConfigArgs.sourceChainSelector, expectedSourceChainConfig); + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + _assertSourceChainConfigEquality( + s_offRamp.getSourceChainConfig(sourceChainConfigArgs.sourceChainSelector), expectedSourceChainConfig + ); + } + + function test_ReplaceExistingChainOnRamp_Success() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + sourceChainConfigs[0].onRamp = ON_RAMP_ADDRESS_2; + + vm.expectEmit(); + emit OffRamp.SourceChainConfigSet( + SOURCE_CHAIN_SELECTOR_1, + OffRamp.SourceChainConfig({router: s_destRouter, isEnabled: true, minSeqNr: 1, onRamp: ON_RAMP_ADDRESS_2}) + ); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + } + + function test_allowNonOnRampUpdateAfterLaneIsUsed_success() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: 2, + merkleRoot: "test #2" + }); + + _commit( + OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }), + s_latestSequenceNumber + ); + + vm.startPrank(OWNER); + + // Allow changes to the Router even after the seqNum is not 1 + assertGt(s_offRamp.getSourceChainConfig(sourceChainConfigs[0].sourceChainSelector).minSeqNr, 1); + + sourceChainConfigs[0].router = IRouter(makeAddr("newRouter")); + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + } + + // Reverts + + function test_ZeroOnRampAddress_Revert() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: new bytes(0), + isEnabled: true + }); + + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + sourceChainConfigs[0].onRamp = abi.encode(address(0)); + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + } + + function test_RouterAddress_Revert() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: IRouter(address(0)), + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + } + + function test_ZeroSourceChainSelector_Revert() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: 0, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + vm.expectRevert(OffRamp.ZeroChainSelectorNotAllowed.selector); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + } + + function test_InvalidOnRampUpdate_Revert() public { + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: 2, + merkleRoot: "test #2" + }); + + _commit( + OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }), + s_latestSequenceNumber + ); + + vm.stopPrank(); + vm.startPrank(OWNER); + + sourceChainConfigs[0].onRamp = ON_RAMP_ADDRESS_2; + + vm.expectRevert(abi.encodeWithSelector(OffRamp.InvalidOnRampUpdate.selector, SOURCE_CHAIN_SELECTOR_1)); + s_offRamp.applySourceChainConfigUpdates(sourceChainConfigs); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.batchExecute.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.batchExecute.t.sol new file mode 100644 index 00000000000..aef54612945 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.batchExecute.t.sol @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../../libraries/Internal.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {Vm} from "forge-std/Vm.sol"; + +contract OffRamp_batchExecute is OffRampSetup { + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); + } + + function test_SingleReport_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + + vm.recordLogs(); + s_offRamp.batchExecute( + _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); + } + + function test_MultipleReportsSameChain_Success() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); + + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); + vm.recordLogs(); + s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + _assertExecutionStateChangedEventLogs( + logs, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, + _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, + _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, + _hashMessage(messages2[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender), nonceBefore); + } + + function test_MultipleReportsDifferentChains_Success() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); + + vm.recordLogs(); + + s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + _assertExecutionStateChangedEventLogs( + logs, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, + _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, + _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, + _hashMessage(messages2[0], ON_RAMP_ADDRESS_3), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + uint64 nonceChain1 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages1[0].sender); + uint64 nonceChain3 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messages2[0].sender); + + assertTrue(nonceChain1 != nonceChain3); + assertGt(nonceChain1, 0); + assertGt(nonceChain3, 0); + } + + function test_MultipleReportsDifferentChainsSkipCursedChain_Success() public { + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); + + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); + + vm.recordLogs(); + + vm.expectEmit(); + emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); + + s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + for (uint256 i = 0; i < logs.length; ++i) { + if (logs[i].topics[0] == OffRamp.ExecutionStateChanged.selector) { + uint64 logSourceChainSelector = uint64(uint256(logs[i].topics[1])); + uint64 logSequenceNumber = uint64(uint256(logs[i].topics[2])); + bytes32 logMessageId = bytes32(logs[i].topics[3]); + (bytes32 logMessageHash, uint8 logState,,) = abi.decode(logs[i].data, (bytes32, uint8, bytes, uint256)); + assertEq(logMessageId, messages2[0].header.messageId); + assertEq(logSourceChainSelector, messages2[0].header.sourceChainSelector); + assertEq(logSequenceNumber, messages2[0].header.sequenceNumber); + assertEq(logMessageId, messages2[0].header.messageId); + assertEq(logMessageHash, _hashMessage(messages2[0], ON_RAMP_ADDRESS_3)); + assertEq(logState, uint8(Internal.MessageExecutionState.SUCCESS)); + } + } + } + + function test_MultipleReportsSkipDuplicate_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.expectEmit(); + emit OffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); + + vm.recordLogs(); + s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_Unhealthy_Success() public { + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); + vm.expectEmit(); + emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); + s_offRamp.batchExecute( + _generateBatchReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ), + new OffRamp.GasLimitOverride[][](1) + ); + + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, false); + + vm.recordLogs(); + s_offRamp.batchExecute( + _generateBatchReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ), + new OffRamp.GasLimitOverride[][](1) + ); + + _assertNoEmit(OffRamp.SkippedReportExecution.selector); + } + + // Reverts + function test_ZeroReports_Revert() public { + vm.expectRevert(OffRamp.EmptyBatch.selector); + s_offRamp.batchExecute(new Internal.ExecutionReport[](0), new OffRamp.GasLimitOverride[][](1)); + } + + function test_OutOfBoundsGasLimitsAccess_Revert() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); + + vm.expectRevert(); + s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](1)); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.ccipReceive.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.ccipReceive.t.sol new file mode 100644 index 00000000000..c05d8ec476a --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.ccipReceive.t.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Client} from "../../../libraries/Client.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +contract OffRamp_ccipReceive is OffRampSetup { + function test_RevertWhen_Always() public { + Client.Any2EVMMessage memory message; + + vm.expectRevert(); + + s_offRamp.ccipReceive(message); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.commit.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.commit.t.sol new file mode 100644 index 00000000000..a942b98cc1e --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.commit.t.sol @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IFeeQuoter} from "../../../interfaces/IFeeQuoter.sol"; +import {IRMNRemote} from "../../../interfaces/IRMNRemote.sol"; + +import {FeeQuoter} from "../../../FeeQuoter.sol"; +import {Internal} from "../../../libraries/Internal.sol"; +import {MultiOCR3Base} from "../../../ocr/MultiOCR3Base.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +contract OffRamp_commit is OffRampSetup { + uint64 internal s_maxInterval = 12; + + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + + s_latestSequenceNumber = uint64(uint256(s_configDigestCommit)); + } + + function test_ReportAndPriceUpdate_Success() public { + OffRamp.CommitReport memory commitReport = _constructCommitReport(); + + vm.expectEmit(); + emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + + assertEq(s_maxInterval + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); + assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); + } + + function test_ReportOnlyRootSuccess_gas() public { + uint64 max1 = 931; + bytes32 root = "Only a single root"; + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: max1, + merkleRoot: root + }); + + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectEmit(); + emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + + assertEq(max1 + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); + assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); + assertEq(block.timestamp, s_offRamp.getMerkleRoot(SOURCE_CHAIN_SELECTOR_1, root)); + } + + function test_RootWithRMNDisabled_success() public { + // force RMN verification to fail + vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); + + // but ☝️ doesn't matter because RMN verification is disabled + OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); + dynamicConfig.isRMNVerificationDisabled = true; + s_offRamp.setDynamicConfig(dynamicConfig); + + uint64 max1 = 931; + bytes32 root = "Only a single root"; + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: max1, + merkleRoot: root + }); + + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectEmit(); + emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + + assertEq(max1 + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); + assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); + assertEq(block.timestamp, s_offRamp.getMerkleRoot(SOURCE_CHAIN_SELECTOR_1, root)); + } + + function test_StaleReportWithRoot_Success() public { + uint64 maxSeq = 12; + uint224 tokenStartPrice = IFeeQuoter(s_offRamp.getDynamicConfig().feeQuoter).getTokenPrice(s_sourceFeeToken).value; + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: maxSeq, + merkleRoot: "stale report 1" + }); + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectEmit(); + emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + + assertEq(maxSeq + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); + assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); + + commitReport.merkleRoots[0].minSeqNr = maxSeq + 1; + commitReport.merkleRoots[0].maxSeqNr = maxSeq * 2; + commitReport.merkleRoots[0].merkleRoot = "stale report 2"; + + vm.expectEmit(); + emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + + assertEq(maxSeq * 2 + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); + assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); + assertEq(tokenStartPrice, IFeeQuoter(s_offRamp.getDynamicConfig().feeQuoter).getTokenPrice(s_sourceFeeToken).value); + } + + function test_OnlyTokenPriceUpdates_Success() public { + // force RMN verification to fail + vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); + OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + + assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); + } + + function test_OnlyGasPriceUpdates_Success() public { + // force RMN verification to fail + vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); + + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); + OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); + } + + function test_PriceSequenceNumberCleared_Success() public { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); + OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); + _commit(commitReport, s_latestSequenceNumber); + + assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); + + vm.startPrank(OWNER); + MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); + ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ + ocrPluginType: uint8(Internal.OCRPluginType.Execution), + configDigest: s_configDigestExec, + F: F, + isSignatureVerificationEnabled: false, + signers: s_emptySigners, + transmitters: s_validTransmitters + }); + s_offRamp.setOCR3Configs(ocrConfigs); + + // Execution plugin OCR config should not clear latest epoch and round + assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); + + // Commit plugin config should clear latest epoch & round + ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ + ocrPluginType: uint8(Internal.OCRPluginType.Commit), + configDigest: s_configDigestCommit, + F: F, + isSignatureVerificationEnabled: true, + signers: s_validSigners, + transmitters: s_validTransmitters + }); + s_offRamp.setOCR3Configs(ocrConfigs); + + assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); + + // The same sequence number can be reported again + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); + + _commit(commitReport, s_latestSequenceNumber); + } + + function test_ValidPriceUpdateThenStaleReportWithRoot_Success() public { + uint64 maxSeq = 12; + uint224 tokenPrice1 = 4e18; + uint224 tokenPrice2 = 5e18; + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); + OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, tokenPrice1), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, tokenPrice1, block.timestamp); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); + + roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: maxSeq, + merkleRoot: "stale report" + }); + commitReport.priceUpdates = _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, tokenPrice2); + commitReport.merkleRoots = roots; + + vm.expectEmit(); + emit OffRamp.CommitReportAccepted(commitReport.merkleRoots, commitReport.priceUpdates); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted(uint8(Internal.OCRPluginType.Commit), s_configDigestCommit, s_latestSequenceNumber); + + _commit(commitReport, s_latestSequenceNumber); + + assertEq(maxSeq + 1, s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR).minSeqNr); + assertEq(tokenPrice1, IFeeQuoter(s_offRamp.getDynamicConfig().feeQuoter).getTokenPrice(s_sourceFeeToken).value); + assertEq(s_latestSequenceNumber, s_offRamp.getLatestPriceSequenceNumber()); + } + + // Reverts + + function test_UnauthorizedTransmitter_Revert() public { + OffRamp.CommitReport memory commitReport = _constructCommitReport(); + + bytes32[3] memory reportContext = + [s_configDigestCommit, bytes32(uint256(s_latestSequenceNumber)), s_configDigestCommit]; + + (bytes32[] memory rs, bytes32[] memory ss,, bytes32 rawVs) = + _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, F + 1); + + vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); + s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); + } + + function test_NoConfig_Revert() public { + _redeployOffRampWithNoOCRConfigs(); + + OffRamp.CommitReport memory commitReport = _constructCommitReport(); + + bytes32[3] memory reportContext = [bytes32(""), s_configDigestCommit, s_configDigestCommit]; + (bytes32[] memory rs, bytes32[] memory ss,, bytes32 rawVs) = + _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, F + 1); + + vm.startPrank(s_validTransmitters[0]); + vm.expectRevert(); + s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); + } + + function test_NoConfigWithOtherConfigPresent_Revert() public { + _redeployOffRampWithNoOCRConfigs(); + + MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); + ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ + ocrPluginType: uint8(Internal.OCRPluginType.Execution), + configDigest: s_configDigestExec, + F: F, + isSignatureVerificationEnabled: false, + signers: s_emptySigners, + transmitters: s_validTransmitters + }); + s_offRamp.setOCR3Configs(ocrConfigs); + + OffRamp.CommitReport memory commitReport = _constructCommitReport(); + + bytes32[3] memory reportContext = [bytes32(""), s_configDigestCommit, s_configDigestCommit]; + (bytes32[] memory rs, bytes32[] memory ss,, bytes32 rawVs) = + _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, F + 1); + + vm.startPrank(s_validTransmitters[0]); + vm.expectRevert(); + s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); + } + + function test_FailedRMNVerification_Reverts() public { + // force RMN verification to fail + vm.mockCallRevert(address(s_mockRMNRemote), abi.encodeWithSelector(IRMNRemote.verify.selector), bytes("")); + + OffRamp.CommitReport memory commitReport = _constructCommitReport(); + vm.expectRevert(); + _commit(commitReport, s_latestSequenceNumber); + } + + function test_Unhealthy_Revert() public { + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + minSeqNr: 1, + maxSeqNr: 2, + merkleRoot: "Only a single root", + onRampAddress: abi.encode(ON_RAMP_ADDRESS_1) + }); + + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.CursedByRMN.selector, roots[0].sourceChainSelector)); + _commit(commitReport, s_latestSequenceNumber); + } + + function test_InvalidRootRevert() public { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: 4, + merkleRoot: bytes32(0) + }); + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectRevert(OffRamp.InvalidRoot.selector); + _commit(commitReport, s_latestSequenceNumber); + } + + function test_InvalidInterval_Revert() public { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 2, + maxSeqNr: 2, + merkleRoot: bytes32(0) + }); + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.InvalidInterval.selector, roots[0].sourceChainSelector, roots[0].minSeqNr, roots[0].maxSeqNr + ) + ); + _commit(commitReport, s_latestSequenceNumber); + } + + function test_InvalidIntervalMinLargerThanMax_Revert() public { + s_offRamp.getSourceChainConfig(SOURCE_CHAIN_SELECTOR); + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: 0, + merkleRoot: bytes32(0) + }); + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.InvalidInterval.selector, roots[0].sourceChainSelector, roots[0].minSeqNr, roots[0].maxSeqNr + ) + ); + _commit(commitReport, s_latestSequenceNumber); + } + + function test_ZeroEpochAndRound_Revert() public { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); + OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + + vm.expectRevert(OffRamp.StaleCommitReport.selector); + _commit(commitReport, 0); + } + + function test_OnlyPriceUpdateStaleReport_Revert() public { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](0); + OffRamp.CommitReport memory commitReport = OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + + vm.expectEmit(); + emit FeeQuoter.UsdPerTokenUpdated(s_sourceFeeToken, 4e18, block.timestamp); + _commit(commitReport, s_latestSequenceNumber); + + vm.expectRevert(OffRamp.StaleCommitReport.selector); + _commit(commitReport, s_latestSequenceNumber); + } + + function test_SourceChainNotEnabled_Revert() public { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: 0, + onRampAddress: abi.encode(ON_RAMP_ADDRESS_1), + minSeqNr: 1, + maxSeqNr: 2, + merkleRoot: "Only a single root" + }); + + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.SourceChainNotEnabled.selector, 0)); + _commit(commitReport, s_latestSequenceNumber); + } + + function test_RootAlreadyCommitted_Revert() public { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: 2, + merkleRoot: "Only a single root" + }); + OffRamp.CommitReport memory commitReport = + OffRamp.CommitReport({priceUpdates: _getEmptyPriceUpdates(), merkleRoots: roots, rmnSignatures: s_rmnSignatures}); + + _commit(commitReport, s_latestSequenceNumber); + commitReport.merkleRoots[0].minSeqNr = 3; + commitReport.merkleRoots[0].maxSeqNr = 3; + + vm.expectRevert( + abi.encodeWithSelector(OffRamp.RootAlreadyCommitted.selector, roots[0].sourceChainSelector, roots[0].merkleRoot) + ); + _commit(commitReport, ++s_latestSequenceNumber); + } + + function test_CommitOnRampMismatch_Revert() public { + OffRamp.CommitReport memory commitReport = _constructCommitReport(); + + commitReport.merkleRoots[0].onRampAddress = ON_RAMP_ADDRESS_2; + + vm.expectRevert(abi.encodeWithSelector(OffRamp.CommitOnRampMismatch.selector, ON_RAMP_ADDRESS_2, ON_RAMP_ADDRESS_1)); + _commit(commitReport, s_latestSequenceNumber); + } + + function _constructCommitReport() internal view returns (OffRamp.CommitReport memory) { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: ON_RAMP_ADDRESS_1, + minSeqNr: 1, + maxSeqNr: s_maxInterval, + merkleRoot: "test #2" + }); + + return OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.constructor.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.constructor.t.sol new file mode 100644 index 00000000000..bd7bb94344c --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.constructor.t.sol @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IRMNRemote} from "../../../interfaces/IRMNRemote.sol"; + +import {Internal} from "../../../libraries/Internal.sol"; +import {MultiOCR3Base} from "../../../ocr/MultiOCR3Base.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampHelper} from "../../helpers/OffRampHelper.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +contract OffRamp_constructor is OffRampSetup { + function test_Constructor_Success() public { + OffRamp.StaticConfig memory staticConfig = OffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) + }); + OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](2); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + sourceChainConfigs[1] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1 + 1, + onRamp: ON_RAMP_ADDRESS_2, + isEnabled: true + }); + + OffRamp.SourceChainConfig memory expectedSourceChainConfig1 = OffRamp.SourceChainConfig({ + router: s_destRouter, + isEnabled: true, + minSeqNr: 1, + onRamp: sourceChainConfigs[0].onRamp + }); + + OffRamp.SourceChainConfig memory expectedSourceChainConfig2 = OffRamp.SourceChainConfig({ + router: s_destRouter, + isEnabled: true, + minSeqNr: 1, + onRamp: sourceChainConfigs[1].onRamp + }); + + uint64[] memory expectedSourceChainSelectors = new uint64[](2); + expectedSourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + expectedSourceChainSelectors[1] = SOURCE_CHAIN_SELECTOR_1 + 1; + + vm.expectEmit(); + emit OffRamp.StaticConfigSet(staticConfig); + + vm.expectEmit(); + emit OffRamp.DynamicConfigSet(dynamicConfig); + + vm.expectEmit(); + emit OffRamp.SourceChainSelectorAdded(SOURCE_CHAIN_SELECTOR_1); + + vm.expectEmit(); + emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1, expectedSourceChainConfig1); + + vm.expectEmit(); + emit OffRamp.SourceChainSelectorAdded(SOURCE_CHAIN_SELECTOR_1 + 1); + + vm.expectEmit(); + emit OffRamp.SourceChainConfigSet(SOURCE_CHAIN_SELECTOR_1 + 1, expectedSourceChainConfig2); + + s_offRamp = new OffRampHelper(staticConfig, dynamicConfig, sourceChainConfigs); + + MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); + ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ + ocrPluginType: uint8(Internal.OCRPluginType.Execution), + configDigest: s_configDigestExec, + F: F, + isSignatureVerificationEnabled: false, + signers: s_emptySigners, + transmitters: s_validTransmitters + }); + + s_offRamp.setOCR3Configs(ocrConfigs); + + // Static config + OffRamp.StaticConfig memory gotStaticConfig = s_offRamp.getStaticConfig(); + assertEq(staticConfig.chainSelector, gotStaticConfig.chainSelector); + assertEq(address(staticConfig.rmnRemote), address(gotStaticConfig.rmnRemote)); + assertEq(staticConfig.tokenAdminRegistry, gotStaticConfig.tokenAdminRegistry); + + // Dynamic config + OffRamp.DynamicConfig memory gotDynamicConfig = s_offRamp.getDynamicConfig(); + _assertSameConfig(dynamicConfig, gotDynamicConfig); + + // OCR Config + MultiOCR3Base.OCRConfig memory expectedOCRConfig = MultiOCR3Base.OCRConfig({ + configInfo: MultiOCR3Base.ConfigInfo({ + configDigest: ocrConfigs[0].configDigest, + F: ocrConfigs[0].F, + n: 0, + isSignatureVerificationEnabled: ocrConfigs[0].isSignatureVerificationEnabled + }), + signers: s_emptySigners, + transmitters: s_validTransmitters + }); + MultiOCR3Base.OCRConfig memory gotOCRConfig = s_offRamp.latestConfigDetails(uint8(Internal.OCRPluginType.Execution)); + _assertOCRConfigEquality(expectedOCRConfig, gotOCRConfig); + + (uint64[] memory actualSourceChainSelectors, OffRamp.SourceChainConfig[] memory actualSourceChainConfigs) = + s_offRamp.getAllSourceChainConfigs(); + + _assertSourceChainConfigEquality(actualSourceChainConfigs[0], expectedSourceChainConfig1); + _assertSourceChainConfigEquality(actualSourceChainConfigs[1], expectedSourceChainConfig2); + + // OffRamp initial values + assertEq("OffRamp 1.6.0-dev", s_offRamp.typeAndVersion()); + assertEq(OWNER, s_offRamp.owner()); + assertEq(0, s_offRamp.getLatestPriceSequenceNumber()); + + // assertion for source chain selector + for (uint256 i = 0; i < expectedSourceChainSelectors.length; i++) { + assertEq(expectedSourceChainSelectors[i], actualSourceChainSelectors[i]); + } + } + + // Revert + function test_ZeroOnRampAddress_Revert() public { + uint64[] memory sourceChainSelectors = new uint64[](1); + sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRamp: new bytes(0), + isEnabled: true + }); + + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + + s_offRamp = new OffRampHelper( + OffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) + }), + _generateDynamicOffRampConfig(address(s_feeQuoter)), + sourceChainConfigs + ); + } + + function test_SourceChainSelector_Revert() public { + uint64[] memory sourceChainSelectors = new uint64[](1); + sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](1); + sourceChainConfigs[0] = OffRamp.SourceChainConfigArgs({ + router: s_destRouter, + sourceChainSelector: 0, + onRamp: ON_RAMP_ADDRESS_1, + isEnabled: true + }); + + vm.expectRevert(OffRamp.ZeroChainSelectorNotAllowed.selector); + + s_offRamp = new OffRampHelper( + OffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) + }), + _generateDynamicOffRampConfig(address(s_feeQuoter)), + sourceChainConfigs + ); + } + + function test_ZeroRMNRemote_Revert() public { + uint64[] memory sourceChainSelectors = new uint64[](1); + sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); + + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + + s_offRamp = new OffRampHelper( + OffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnRemote: IRMNRemote(address(0)), + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) + }), + _generateDynamicOffRampConfig(address(s_feeQuoter)), + sourceChainConfigs + ); + } + + function test_ZeroChainSelector_Revert() public { + uint64[] memory sourceChainSelectors = new uint64[](1); + sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); + + vm.expectRevert(OffRamp.ZeroChainSelectorNotAllowed.selector); + + s_offRamp = new OffRampHelper( + OffRamp.StaticConfig({ + chainSelector: 0, + rmnRemote: s_mockRMNRemote, + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(s_inboundNonceManager) + }), + _generateDynamicOffRampConfig(address(s_feeQuoter)), + sourceChainConfigs + ); + } + + function test_ZeroTokenAdminRegistry_Revert() public { + uint64[] memory sourceChainSelectors = new uint64[](1); + sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); + + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + + s_offRamp = new OffRampHelper( + OffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + tokenAdminRegistry: address(0), + nonceManager: address(s_inboundNonceManager) + }), + _generateDynamicOffRampConfig(address(s_feeQuoter)), + sourceChainConfigs + ); + } + + function test_ZeroNonceManager_Revert() public { + uint64[] memory sourceChainSelectors = new uint64[](1); + sourceChainSelectors[0] = SOURCE_CHAIN_SELECTOR_1; + + OffRamp.SourceChainConfigArgs[] memory sourceChainConfigs = new OffRamp.SourceChainConfigArgs[](0); + + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + + s_offRamp = new OffRampHelper( + OffRamp.StaticConfig({ + chainSelector: DEST_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + tokenAdminRegistry: address(s_tokenAdminRegistry), + nonceManager: address(0) + }), + _generateDynamicOffRampConfig(address(s_feeQuoter)), + sourceChainConfigs + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.execute.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.execute.t.sol new file mode 100644 index 00000000000..9fd2499ef28 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.execute.t.sol @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IMessageInterceptor} from "../../../interfaces/IMessageInterceptor.sol"; + +import {Internal} from "../../../libraries/Internal.sol"; +import {MultiOCR3Base} from "../../../ocr/MultiOCR3Base.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {Vm} from "forge-std/Vm.sol"; + +contract OffRamp_execute is OffRampSetup { + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + } + + // Asserts that execute completes + function test_SingleReport_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted( + uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) + ); + + vm.recordLogs(); + + _execute(reports); + + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_MultipleReports_Success() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted( + uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) + ); + + vm.recordLogs(); + _execute(reports); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + _assertExecutionStateChangedEventLogs( + logs, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, + _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, + _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, + _hashMessage(messages2[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_LargeBatch_Success() public { + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](10); + for (uint64 i = 0; i < reports.length; ++i) { + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](3); + messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1 + i * 3); + messages[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2 + i * 3); + messages[2] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3 + i * 3); + + reports[i] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + } + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted( + uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) + ); + + vm.recordLogs(); + _execute(reports); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + for (uint64 i = 0; i < reports.length; ++i) { + for (uint64 j = 0; j < reports[i].messages.length; ++j) { + _assertExecutionStateChangedEventLogs( + logs, + reports[i].messages[j].header.sourceChainSelector, + reports[i].messages[j].header.sequenceNumber, + reports[i].messages[j].header.messageId, + _hashMessage(reports[i].messages[j], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + } + } + + function test_MultipleReportsWithPartialValidationFailures_Success() public { + _enableInboundMessageInterceptor(); + + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 3); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages2); + + s_inboundMessageInterceptor.setMessageIdValidationState(messages1[0].header.messageId, true); + s_inboundMessageInterceptor.setMessageIdValidationState(messages2[0].header.messageId, true); + + vm.expectEmit(); + emit MultiOCR3Base.Transmitted( + uint8(Internal.OCRPluginType.Execution), s_configDigestExec, uint64(uint256(s_configDigestExec)) + ); + + vm.recordLogs(); + _execute(reports); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + _assertExecutionStateChangedEventLogs( + logs, + messages1[0].header.sourceChainSelector, + messages1[0].header.sequenceNumber, + messages1[0].header.messageId, + _hashMessage(messages1[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector( + IMessageInterceptor.MessageValidationError.selector, + abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) + ) + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages1[1].header.sourceChainSelector, + messages1[1].header.sequenceNumber, + messages1[1].header.messageId, + _hashMessage(messages1[1], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + messages2[0].header.sourceChainSelector, + messages2[0].header.sequenceNumber, + messages2[0].header.messageId, + _hashMessage(messages2[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector( + IMessageInterceptor.MessageValidationError.selector, + abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) + ) + ); + } + + // Reverts + + function test_UnauthorizedTransmitter_Revert() public { + bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; + + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); + s_offRamp.execute(reportContext, abi.encode(reports)); + } + + function test_NoConfig_Revert() public { + _redeployOffRampWithNoOCRConfigs(); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + bytes32[3] memory reportContext = [bytes32(""), s_configDigestExec, s_configDigestExec]; + + vm.startPrank(s_validTransmitters[0]); + vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); + s_offRamp.execute(reportContext, abi.encode(reports)); + } + + function test_NoConfigWithOtherConfigPresent_Revert() public { + _redeployOffRampWithNoOCRConfigs(); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + + MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); + ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ + ocrPluginType: uint8(Internal.OCRPluginType.Commit), + configDigest: s_configDigestCommit, + F: F, + isSignatureVerificationEnabled: true, + signers: s_validSigners, + transmitters: s_validTransmitters + }); + s_offRamp.setOCR3Configs(ocrConfigs); + + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + bytes32[3] memory reportContext = [bytes32(""), s_configDigestExec, s_configDigestExec]; + + vm.startPrank(s_validTransmitters[0]); + vm.expectRevert(MultiOCR3Base.UnauthorizedTransmitter.selector); + s_offRamp.execute(reportContext, abi.encode(reports)); + } + + function test_WrongConfigWithSigners_Revert() public { + _redeployOffRampWithNoOCRConfigs(); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + + s_configDigestExec = _getBasicConfigDigest(1, s_validSigners, s_validTransmitters); + + MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](1); + ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ + ocrPluginType: uint8(Internal.OCRPluginType.Execution), + configDigest: s_configDigestExec, + F: F, + isSignatureVerificationEnabled: true, + signers: s_validSigners, + transmitters: s_validTransmitters + }); + + vm.expectRevert(OffRamp.SignatureVerificationNotAllowedInExecutionPlugin.selector); + s_offRamp.setOCR3Configs(ocrConfigs); + } + + function test_ZeroReports_Revert() public { + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](0); + + vm.expectRevert(OffRamp.EmptyBatch.selector); + _execute(reports); + } + + function test_IncorrectArrayType_Revert() public { + bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; + + uint256[] memory wrongData = new uint256[](2); + wrongData[0] = 1; + + vm.startPrank(s_validTransmitters[0]); + vm.expectRevert(); + s_offRamp.execute(reportContext, abi.encode(wrongData)); + } + + function test_NonArray_Revert() public { + bytes32[3] memory reportContext = [s_configDigestExec, s_configDigestExec, s_configDigestExec]; + + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.startPrank(s_validTransmitters[0]); + vm.expectRevert(); + s_offRamp.execute(reportContext, abi.encode(report)); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.executeSingleMessage.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.executeSingleMessage.t.sol new file mode 100644 index 00000000000..45fa18930d9 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.executeSingleMessage.t.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IMessageInterceptor} from "../../../interfaces/IMessageInterceptor.sol"; +import {IRouter} from "../../../interfaces/IRouter.sol"; + +import {Client} from "../../../libraries/Client.sol"; +import {Internal} from "../../../libraries/Internal.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {MaybeRevertMessageReceiverNo165} from "../../helpers/receivers/MaybeRevertMessageReceiverNo165.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +contract OffRamp_executeSingleMessage is OffRampSetup { + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + vm.startPrank(address(s_offRamp)); + } + + function test_executeSingleMessage_NoTokens_Success() public { + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + + Client.Any2EVMMessage memory expectedAny2EvmMessage = Client.Any2EVMMessage({ + messageId: message.header.messageId, + sourceChainSelector: message.header.sourceChainSelector, + sender: message.sender, + data: message.data, + destTokenAmounts: new Client.EVMTokenAmount[](0) + }); + vm.expectCall( + address(s_destRouter), + abi.encodeWithSelector( + IRouter.routeMessage.selector, + expectedAny2EvmMessage, + Internal.GAS_FOR_CALL_EXACT_CHECK, + message.gasLimit, + message.receiver + ) + ); + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + function test_executeSingleMessage_WithTokens_Success() public { + Internal.Any2EVMRampMessage memory message = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1)[0]; + bytes[] memory offchainTokenData = new bytes[](message.tokenAmounts.length); + + vm.expectCall( + s_destPoolByToken[s_destTokens[0]], + abi.encodeWithSelector( + LockReleaseTokenPool.releaseOrMint.selector, + Pool.ReleaseOrMintInV1({ + originalSender: message.sender, + receiver: message.receiver, + amount: message.tokenAmounts[0].amount, + localToken: message.tokenAmounts[0].destTokenAddress, + remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, + sourcePoolAddress: message.tokenAmounts[0].sourcePoolAddress, + sourcePoolData: message.tokenAmounts[0].extraData, + offchainTokenData: offchainTokenData[0] + }) + ) + ); + + s_offRamp.executeSingleMessage(message, offchainTokenData, new uint32[](0)); + } + + function test_executeSingleMessage_WithVInterception_Success() public { + vm.stopPrank(); + vm.startPrank(OWNER); + _enableInboundMessageInterceptor(); + vm.startPrank(address(s_offRamp)); + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + function test_NonContract_Success() public { + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + message.receiver = STRANGER; + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + function test_NonContractWithTokens_Success() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1000; + amounts[1] = 50; + vm.expectEmit(); + emit TokenPool.Released(address(s_offRamp), STRANGER, amounts[0]); + vm.expectEmit(); + emit TokenPool.Minted(address(s_offRamp), STRANGER, amounts[1]); + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + message.receiver = STRANGER; + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + // Reverts + + function test_TokenHandlingError_Revert() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1000; + amounts[1] = 50; + + bytes memory errorMessage = "Random token pool issue"; + + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + s_maybeRevertingPool.setShouldRevert(errorMessage); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, errorMessage)); + + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + function test_ZeroGasDONExecution_Revert() public { + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + message.gasLimit = 0; + + vm.expectRevert(abi.encodeWithSelector(OffRamp.ReceiverError.selector, "")); + + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + function test_MessageSender_Revert() public { + vm.stopPrank(); + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + vm.expectRevert(OffRamp.CanOnlySelfCall.selector); + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + function test_executeSingleMessage_WithFailingValidation_Revert() public { + vm.stopPrank(); + vm.startPrank(OWNER); + _enableInboundMessageInterceptor(); + vm.startPrank(address(s_offRamp)); + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + s_inboundMessageInterceptor.setMessageIdValidationState(message.header.messageId, true); + vm.expectRevert( + abi.encodeWithSelector( + IMessageInterceptor.MessageValidationError.selector, + abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) + ) + ); + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } + + function test_executeSingleMessage_WithFailingValidationNoRouterCall_Revert() public { + vm.stopPrank(); + vm.startPrank(OWNER); + _enableInboundMessageInterceptor(); + vm.startPrank(address(s_offRamp)); + + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + + // Setup the receiver to a non-CCIP Receiver, which will skip the Router call (but should still perform the validation) + MaybeRevertMessageReceiverNo165 newReceiver = new MaybeRevertMessageReceiverNo165(true); + message.receiver = address(newReceiver); + message.header.messageId = _hashMessage(message, ON_RAMP_ADDRESS_1); + + s_inboundMessageInterceptor.setMessageIdValidationState(message.header.messageId, true); + vm.expectRevert( + abi.encodeWithSelector( + IMessageInterceptor.MessageValidationError.selector, + abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) + ) + ); + s_offRamp.executeSingleMessage(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.executeSingleReport.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.executeSingleReport.t.sol new file mode 100644 index 00000000000..e651ad3836a --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.executeSingleReport.t.sol @@ -0,0 +1,691 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {CallWithExactGas} from "../../../../shared/call/CallWithExactGas.sol"; +import {NonceManager} from "../../../NonceManager.sol"; +import {Client} from "../../../libraries/Client.sol"; +import {Internal} from "../../../libraries/Internal.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {ConformingReceiver} from "../../helpers/receivers/ConformingReceiver.sol"; +import {MaybeRevertMessageReceiver} from "../../helpers/receivers/MaybeRevertMessageReceiver.sol"; +import {MaybeRevertMessageReceiverNo165} from "../../helpers/receivers/MaybeRevertMessageReceiverNo165.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {Vm} from "forge-std/Vm.sol"; + +contract OffRamp_executeSingleReport is OffRampSetup { + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); + } + + function test_SingleMessageNoTokens_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + messages[0].header.nonce++; + messages[0].header.sequenceNumber++; + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), nonceBefore); + } + + function test_SingleMessageNoTokensUnordered_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].header.nonce = 0; + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + // Nonce never increments on unordered messages. + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + assertEq( + s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), + nonceBefore, + "nonce must remain unchanged on unordered messages" + ); + + messages[0].header.sequenceNumber++; + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + // Nonce never increments on unordered messages. + nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender); + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + assertEq( + s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender), + nonceBefore, + "nonce must remain unchanged on unordered messages" + ); + } + + function test_SingleMessageNoTokensOtherChain_Success() public { + Internal.Any2EVMRampMessage[] memory messagesChain1 = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messagesChain1), new OffRamp.GasLimitOverride[](0) + ); + + uint64 nonceChain1 = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender); + assertGt(nonceChain1, 0); + + Internal.Any2EVMRampMessage[] memory messagesChain2 = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); + assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messagesChain2), new OffRamp.GasLimitOverride[](0) + ); + assertGt(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_3, messagesChain2[0].sender), 0); + + // Other chain's nonce is unaffected + assertEq(s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messagesChain1[0].sender), nonceChain1); + } + + function test_ReceiverError_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + bytes memory realError1 = new bytes(2); + realError1[0] = 0xbe; + realError1[1] = 0xef; + s_reverting_receiver.setErr(realError1); + + messages[0].receiver = address(s_reverting_receiver); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + // Nonce should increment on non-strict + assertEq(uint64(0), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector( + OffRamp.ReceiverError.selector, + abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, realError1) + ) + ); + assertEq(uint64(1), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); + } + + function test_SkippedIncorrectNonce_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + messages[0].header.nonce++; + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + vm.expectEmit(); + emit NonceManager.SkippedIncorrectNonce( + messages[0].header.sourceChainSelector, messages[0].header.nonce, messages[0].sender + ); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + } + + function test_SkippedIncorrectNonceStillExecutes_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + messages[1].header.nonce++; + messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); + + vm.expectEmit(); + emit NonceManager.SkippedIncorrectNonce(SOURCE_CHAIN_SELECTOR_1, messages[1].header.nonce, messages[1].sender); + + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test__execute_SkippedAlreadyExecutedMessage_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + vm.expectEmit(); + emit OffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + } + + function test__execute_SkippedAlreadyExecutedMessageUnordered_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].header.nonce = 0; + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + vm.expectEmit(); + emit OffRamp.SkippedAlreadyExecutedMessage(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + } + + // Send a message to a contract that does not implement the CCIPReceiver interface + // This should execute successfully. + function test_SingleMessageToNonCCIPReceiver_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + MaybeRevertMessageReceiverNo165 newReceiver = new MaybeRevertMessageReceiverNo165(true); + messages[0].receiver = address(newReceiver); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_SingleMessagesNoTokensSuccess_gas() public { + vm.pauseGasMetering(); + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.resumeGasMetering(); + vm.recordLogs(); + s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_TwoMessagesWithTokensSuccess_gas() public { + vm.pauseGasMetering(); + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + // Set message 1 to use another receiver to simulate more fair gas costs + messages[1].receiver = address(s_secondary_receiver); + messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); + + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.resumeGasMetering(); + vm.recordLogs(); + s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[1].header.sequenceNumber, + messages[1].header.messageId, + _hashMessage(messages[1], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_TwoMessagesWithTokensAndGE_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + // Set message 1 to use another receiver to simulate more fair gas costs + messages[1].receiver = address(s_secondary_receiver); + messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); + + assertEq(uint64(0), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); + + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) + ); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[1].header.sequenceNumber, + messages[1].header.messageId, + _hashMessage(messages[1], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + assertEq(uint64(2), s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER))); + } + + function test_Fuzz_InterleavingOrderedAndUnorderedMessages_Success( + bool[7] memory orderings + ) public { + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](orderings.length); + // number of tokens needs to be capped otherwise we hit UnsupportedNumberOfTokens. + Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](3); + for (uint256 i = 0; i < 3; ++i) { + tokenAmounts[i].token = s_sourceTokens[i % s_sourceTokens.length]; + tokenAmounts[i].amount = 1e18; + } + uint64 expectedNonce = 0; + + for (uint256 i = 0; i < orderings.length; ++i) { + messages[i] = + _generateAny2EVMMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, uint64(i + 1), tokenAmounts, !orderings[i]); + if (orderings[i]) { + messages[i].header.nonce = ++expectedNonce; + } + messages[i].header.messageId = _hashMessage(messages[i], ON_RAMP_ADDRESS_1); + } + + uint64 nonceBefore = s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER)); + assertEq(uint64(0), nonceBefore, "nonce before exec should be 0"); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) + ); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + // all executions should succeed. + for (uint256 i = 0; i < orderings.length; ++i) { + assertEq( + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, messages[i].header.sequenceNumber)), + uint256(Internal.MessageExecutionState.SUCCESS) + ); + + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[i].header.sequenceNumber, + messages[i].header.messageId, + _hashMessage(messages[i], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + assertEq( + nonceBefore + expectedNonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, abi.encode(OWNER)) + ); + } + + function test_InvalidSourcePoolAddress_Success() public { + address fakePoolAddress = address(0x0000000000333333); + + Internal.Any2EVMRampMessage[] memory messages = + _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].tokenAmounts[0].sourcePoolAddress = abi.encode(fakePoolAddress); + + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector( + OffRamp.TokenHandlingError.selector, + abi.encodeWithSelector(TokenPool.InvalidSourcePoolAddress.selector, abi.encode(fakePoolAddress)) + ) + ); + } + + function test_WithCurseOnAnotherSourceChain_Success() public { + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_2, true); + s_offRamp.executeSingleReport( + _generateReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ), + new OffRamp.GasLimitOverride[](0) + ); + } + + function test_Unhealthy_Success() public { + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); + + vm.expectEmit(); + emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); + s_offRamp.executeSingleReport( + _generateReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ), + new OffRamp.GasLimitOverride[](0) + ); + + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, false); + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ), + new OffRamp.GasLimitOverride[](0) + ); + + _assertNoEmit(OffRamp.SkippedReportExecution.selector); + } + + // Reverts + + function test_MismatchingDestChainSelector_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3); + messages[0].header.destChainSelector = DEST_CHAIN_SELECTOR + 1; + + Internal.ExecutionReport memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.expectRevert( + abi.encodeWithSelector(OffRamp.InvalidMessageDestChainSelector.selector, messages[0].header.destChainSelector) + ); + s_offRamp.executeSingleReport(executionReport, new OffRamp.GasLimitOverride[](0)); + } + + function test_UnhealthySingleChainCurse_Revert() public { + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, true); + vm.expectEmit(); + emit OffRamp.SkippedReportExecution(SOURCE_CHAIN_SELECTOR_1); + s_offRamp.executeSingleReport( + _generateReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ), + new OffRamp.GasLimitOverride[](0) + ); + vm.recordLogs(); + // Uncurse should succeed + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_1, false); + s_offRamp.executeSingleReport( + _generateReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateMessagesWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ), + new OffRamp.GasLimitOverride[](0) + ); + _assertNoEmit(OffRamp.SkippedReportExecution.selector); + } + + function test_UnexpectedTokenData_Revert() public { + Internal.ExecutionReport memory report = _generateReportFromMessages( + SOURCE_CHAIN_SELECTOR_1, _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1) + ); + report.offchainTokenData = new bytes[][](report.messages.length + 1); + + vm.expectRevert(OffRamp.UnexpectedTokenData.selector); + + s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); + } + + function test_EmptyReport_Revert() public { + vm.expectRevert(abi.encodeWithSelector(OffRamp.EmptyReport.selector, SOURCE_CHAIN_SELECTOR_1)); + + s_offRamp.executeSingleReport( + Internal.ExecutionReport({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + proofs: new bytes32[](0), + proofFlagBits: 0, + messages: new Internal.Any2EVMRampMessage[](0), + offchainTokenData: new bytes[][](0) + }), + new OffRamp.GasLimitOverride[](0) + ); + } + + function test_RootNotCommitted_Revert() public { + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 0); + vm.expectRevert(abi.encodeWithSelector(OffRamp.RootNotCommitted.selector, SOURCE_CHAIN_SELECTOR_1)); + + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) + ); + } + + function test_ManualExecutionNotYetEnabled_Revert() public { + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, BLOCK_TIME); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.ManualExecutionNotYetEnabled.selector, SOURCE_CHAIN_SELECTOR_1)); + + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), _getGasLimitsFromMessages(messages) + ); + } + + function test_NonExistingSourceChain_Revert() public { + uint64 newSourceChainSelector = SOURCE_CHAIN_SELECTOR_1 + 1; + bytes memory newOnRamp = abi.encode(ON_RAMP_ADDRESS, 1); + + Internal.Any2EVMRampMessage[] memory messages = _generateSingleBasicMessage(newSourceChainSelector, newOnRamp); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.SourceChainNotEnabled.selector, newSourceChainSelector)); + s_offRamp.executeSingleReport( + _generateReportFromMessages(newSourceChainSelector, messages), new OffRamp.GasLimitOverride[](0) + ); + } + + function test_DisabledSourceChain_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_2, ON_RAMP_ADDRESS_2); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.SourceChainNotEnabled.selector, SOURCE_CHAIN_SELECTOR_2)); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_2, messages), new OffRamp.GasLimitOverride[](0) + ); + } + + function test_TokenDataMismatch_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + report.offchainTokenData[0] = new bytes[](messages[0].tokenAmounts.length + 1); + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.TokenDataMismatch.selector, SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber + ) + ); + s_offRamp.executeSingleReport(report, new OffRamp.GasLimitOverride[](0)); + } + + function test_RouterYULCall_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + // gas limit too high, Router's external call should revert + messages[0].gasLimit = 1e36; + messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + Internal.ExecutionReport memory executionReport = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + vm.recordLogs(); + s_offRamp.executeSingleReport(executionReport, new OffRamp.GasLimitOverride[](0)); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector(CallWithExactGas.NotEnoughGasForCall.selector) + ); + } + + function test_RetryFailedMessageWithoutManualExecution_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + bytes memory realError1 = new bytes(2); + realError1[0] = 0xbe; + realError1[1] = 0xef; + s_reverting_receiver.setErr(realError1); + + messages[0].receiver = address(s_reverting_receiver); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + _assertExecutionStateChangedEventLogs( + messages[0].header.sourceChainSelector, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector( + OffRamp.ReceiverError.selector, + abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, realError1) + ) + ); + + // The second time should skip the msg + vm.expectEmit(); + emit OffRamp.AlreadyAttempted(SOURCE_CHAIN_SELECTOR_1, messages[0].header.sequenceNumber); + + s_offRamp.executeSingleReport( + _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[](0) + ); + } + + function _constructCommitReport( + bytes32 merkleRoot + ) internal view returns (OffRamp.CommitReport memory) { + Internal.MerkleRoot[] memory roots = new Internal.MerkleRoot[](1); + roots[0] = Internal.MerkleRoot({ + sourceChainSelector: SOURCE_CHAIN_SELECTOR_1, + onRampAddress: abi.encode(ON_RAMP_ADDRESS_1), + minSeqNr: 1, + maxSeqNr: 2, + merkleRoot: merkleRoot + }); + + return OffRamp.CommitReport({ + priceUpdates: _getSingleTokenPriceUpdateStruct(s_sourceFeeToken, 4e18), + merkleRoots: roots, + rmnSignatures: s_rmnSignatures + }); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.getExecutionState.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.getExecutionState.t.sol new file mode 100644 index 00000000000..9b8e719053b --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.getExecutionState.t.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../../libraries/Internal.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +contract OffRamp_getExecutionState is OffRampSetup { + mapping(uint64 sourceChainSelector => mapping(uint64 seqNum => Internal.MessageExecutionState state)) internal + s_differentialExecutionState; + + /// forge-config: default.fuzz.runs = 32 + /// forge-config: ccip.fuzz.runs = 32 + function test_Fuzz_Differential_Success( + uint64 sourceChainSelector, + uint16[500] memory seqNums, + uint8[500] memory values + ) public { + for (uint256 i = 0; i < seqNums.length; ++i) { + // Only use the first three slots. This makes sure existing slots get overwritten + // as the tests uses 500 sequence numbers. + uint16 seqNum = seqNums[i] % 386; + Internal.MessageExecutionState state = Internal.MessageExecutionState(values[i] % 4); + s_differentialExecutionState[sourceChainSelector][seqNum] = state; + s_offRamp.setExecutionStateHelper(sourceChainSelector, seqNum, state); + assertEq(uint256(state), uint256(s_offRamp.getExecutionState(sourceChainSelector, seqNum))); + } + + for (uint256 i = 0; i < seqNums.length; ++i) { + uint16 seqNum = seqNums[i] % 386; + Internal.MessageExecutionState expectedState = s_differentialExecutionState[sourceChainSelector][seqNum]; + assertEq(uint256(expectedState), uint256(s_offRamp.getExecutionState(sourceChainSelector, seqNum))); + } + } + + function test_GetExecutionState_Success() public { + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 0, Internal.MessageExecutionState.FAILURE); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 1, Internal.MessageExecutionState.FAILURE); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (3 << 2)); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 1, Internal.MessageExecutionState.IN_PROGRESS); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2)); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 2, Internal.MessageExecutionState.FAILURE); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2) + (3 << 4)); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 127, Internal.MessageExecutionState.IN_PROGRESS); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2) + (3 << 4) + (1 << 254)); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 128, Internal.MessageExecutionState.SUCCESS); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 2) + (3 << 4) + (1 << 254)); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 1), 2); + + assertEq( + uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 0)) + ); + assertEq( + uint256(Internal.MessageExecutionState.IN_PROGRESS), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 1)) + ); + assertEq( + uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 2)) + ); + assertEq( + uint256(Internal.MessageExecutionState.IN_PROGRESS), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 127)) + ); + assertEq( + uint256(Internal.MessageExecutionState.SUCCESS), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 128)) + ); + } + + function test_GetDifferentChainExecutionState_Success() public { + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 0, Internal.MessageExecutionState.FAILURE); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), 0); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 127, Internal.MessageExecutionState.IN_PROGRESS); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 254)); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), 0); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, 128, Internal.MessageExecutionState.SUCCESS); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 254)); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 1), 2); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), 0); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 1), 0); + + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1 + 1, 127, Internal.MessageExecutionState.FAILURE); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 0), 3 + (1 << 254)); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, 1), 2); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 0), (3 << 254)); + assertEq(s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1 + 1, 1), 0); + + assertEq( + uint256(Internal.MessageExecutionState.FAILURE), uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 0)) + ); + assertEq( + uint256(Internal.MessageExecutionState.IN_PROGRESS), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 127)) + ); + assertEq( + uint256(Internal.MessageExecutionState.SUCCESS), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, 128)) + ); + + assertEq( + uint256(Internal.MessageExecutionState.UNTOUCHED), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1 + 1, 0)) + ); + assertEq( + uint256(Internal.MessageExecutionState.FAILURE), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1 + 1, 127)) + ); + assertEq( + uint256(Internal.MessageExecutionState.UNTOUCHED), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1 + 1, 128)) + ); + } + + function test_FillExecutionState_Success() public { + for (uint64 i = 0; i < 384; ++i) { + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, i, Internal.MessageExecutionState.FAILURE); + } + + for (uint64 i = 0; i < 384; ++i) { + assertEq( + uint256(Internal.MessageExecutionState.FAILURE), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, i)) + ); + } + + for (uint64 i = 0; i < 3; ++i) { + assertEq(type(uint256).max, s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, i)); + } + + for (uint64 i = 0; i < 384; ++i) { + s_offRamp.setExecutionStateHelper(SOURCE_CHAIN_SELECTOR_1, i, Internal.MessageExecutionState.IN_PROGRESS); + } + + for (uint64 i = 0; i < 384; ++i) { + assertEq( + uint256(Internal.MessageExecutionState.IN_PROGRESS), + uint256(s_offRamp.getExecutionState(SOURCE_CHAIN_SELECTOR_1, i)) + ); + } + + for (uint64 i = 0; i < 3; ++i) { + // 0x555... == 0b101010101010..... + assertEq( + 0x5555555555555555555555555555555555555555555555555555555555555555, + s_offRamp.getExecutionStateBitMap(SOURCE_CHAIN_SELECTOR_1, i) + ); + } + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.manuallyExecute.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.manuallyExecute.t.sol new file mode 100644 index 00000000000..0422053bdd7 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.manuallyExecute.t.sol @@ -0,0 +1,586 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../../libraries/Internal.sol"; +import {MultiOCR3Base} from "../../../ocr/MultiOCR3Base.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {ConformingReceiver} from "../../helpers/receivers/ConformingReceiver.sol"; +import {MaybeRevertMessageReceiver} from "../../helpers/receivers/MaybeRevertMessageReceiver.sol"; +import {ReentrancyAbuserMultiRamp} from "../../helpers/receivers/ReentrancyAbuserMultiRamp.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {Vm} from "forge-std/Vm.sol"; + +contract OffRamp_manuallyExecute is OffRampSetup { + uint32 internal constant MAX_TOKEN_POOL_RELEASE_OR_MINT_GAS = 200_000; + + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_1, 1); + s_offRamp.setVerifyOverrideResult(SOURCE_CHAIN_SELECTOR_3, 1); + } + + function test_manuallyExecute_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].receiver = address(s_reverting_receiver); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + s_offRamp.batchExecute( + _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) + ); + + s_reverting_receiver.setRevert(false); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](messages.length); + + vm.recordLogs(); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_manuallyExecute_WithGasOverride_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].receiver = address(s_reverting_receiver); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + s_offRamp.batchExecute( + _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) + ); + + s_reverting_receiver.setRevert(false); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + gasLimitOverrides[0][0].receiverExecutionGasLimit += 1; + vm.recordLogs(); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_manuallyExecute_DoesNotRevertIfUntouched_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].receiver = address(s_reverting_receiver); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + assertEq( + messages[0].header.nonce - 1, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender) + ); + + s_reverting_receiver.setRevert(true); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + + vm.recordLogs(); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector( + OffRamp.ReceiverError.selector, abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, "") + ) + ); + + assertEq( + messages[0].header.nonce, s_inboundNonceManager.getInboundNonce(SOURCE_CHAIN_SELECTOR_1, messages[0].sender) + ); + } + + function test_manuallyExecute_WithMultiReportGasOverride_Success() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](3); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](2); + + for (uint64 i = 0; i < 3; ++i) { + messages1[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); + messages1[i].receiver = address(s_reverting_receiver); + messages1[i].header.messageId = _hashMessage(messages1[i], ON_RAMP_ADDRESS_1); + } + + for (uint64 i = 0; i < 2; ++i) { + messages2[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, i + 1); + messages2[i].receiver = address(s_reverting_receiver); + messages2[i].header.messageId = _hashMessage(messages2[i], ON_RAMP_ADDRESS_3); + } + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); + + s_offRamp.batchExecute(reports, new OffRamp.GasLimitOverride[][](2)); + + s_reverting_receiver.setRevert(false); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages1); + gasLimitOverrides[1] = _getGasLimitsFromMessages(messages2); + + for (uint256 i = 0; i < 3; ++i) { + gasLimitOverrides[0][i].receiverExecutionGasLimit += 1; + } + + for (uint256 i = 0; i < 2; ++i) { + gasLimitOverrides[1][i].receiverExecutionGasLimit += 1; + } + + vm.recordLogs(); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + for (uint256 j = 0; j < 3; ++j) { + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages1[j].header.sequenceNumber, + messages1[j].header.messageId, + _hashMessage(messages1[j], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + for (uint256 k = 0; k < 2; ++k) { + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_3, + messages2[k].header.sequenceNumber, + messages2[k].header.messageId, + _hashMessage(messages2[k], ON_RAMP_ADDRESS_3), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + } + + function test_manuallyExecute_WithPartialMessages_Success() public { + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](3); + + for (uint64 i = 0; i < 3; ++i) { + messages[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); + } + + messages[1].receiver = address(s_reverting_receiver); + messages[1].header.messageId = _hashMessage(messages[1], ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + s_offRamp.batchExecute( + _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) + ); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[1].header.sequenceNumber, + messages[1].header.messageId, + _hashMessage(messages[1], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector( + OffRamp.ReceiverError.selector, + abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, bytes("")) + ) + ); + + _assertExecutionStateChangedEventLogs( + logs, + SOURCE_CHAIN_SELECTOR_1, + messages[2].header.sequenceNumber, + messages[2].header.messageId, + _hashMessage(messages[2], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + s_reverting_receiver.setRevert(false); + + // Only the 2nd message reverted + Internal.Any2EVMRampMessage[] memory newMessages = new Internal.Any2EVMRampMessage[](1); + newMessages[0] = messages[1]; + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(newMessages); + gasLimitOverrides[0][0].receiverExecutionGasLimit += 1; + + vm.recordLogs(); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, newMessages), gasLimitOverrides); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + function test_manuallyExecute_LowGasLimit_Success() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + messages[0].gasLimit = 1; + messages[0].receiver = address(new ConformingReceiver(address(s_destRouter), s_destFeeToken)); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + vm.recordLogs(); + s_offRamp.batchExecute( + _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) + ); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.FAILURE, + abi.encodeWithSelector(OffRamp.ReceiverError.selector, "") + ); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](1); + gasLimitOverrides[0][0].receiverExecutionGasLimit = 100_000; + + vm.expectEmit(); + emit ConformingReceiver.MessageReceived(); + + vm.recordLogs(); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + } + + // Reverts + + function test_manuallyExecute_ForkedChain_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + uint256 chain1 = block.chainid; + uint256 chain2 = chain1 + 1; + vm.chainId(chain2); + vm.expectRevert(abi.encodeWithSelector(MultiOCR3Base.ForkedChain.selector, chain1, chain2)); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + } + + function test_ManualExecGasLimitMismatchSingleReport_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](2); + messages[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + + Internal.ExecutionReport[] memory reports = _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + // No overrides for report + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, new OffRamp.GasLimitOverride[][](0)); + + // No messages + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + + // 1 message missing + gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](1); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + + // 1 message in excess + gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](3); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + } + + function test_manuallyExecute_GasLimitMismatchMultipleReports_Revert() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](2); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages1[1] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 2); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, 1); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, new OffRamp.GasLimitOverride[][](0)); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, new OffRamp.GasLimitOverride[][](1)); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + + // 2nd report empty + gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](2); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + + // 1st report empty + gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](0); + gasLimitOverrides[1] = new OffRamp.GasLimitOverride[](1); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + + // 1st report oversized + gasLimitOverrides[0] = new OffRamp.GasLimitOverride[](3); + + vm.expectRevert(OffRamp.ManualExecutionGasLimitMismatch.selector); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + } + + function test_manuallyExecute_InvalidReceiverExecutionGasLimit_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + gasLimitOverrides[0][0].receiverExecutionGasLimit--; + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.InvalidManualExecutionGasLimit.selector, + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.messageId, + gasLimitOverrides[0][0].receiverExecutionGasLimit + ) + ); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + } + + function test_manuallyExecute_DestinationGasAmountCountMismatch_Revert() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1000; + amounts[1] = 1000; + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](1); + messages[0] = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + // empty tokenGasOverride array provided + vm.expectRevert( + abi.encodeWithSelector(OffRamp.ManualExecutionGasAmountCountMismatch.selector, messages[0].header.messageId, 1) + ); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + + //trying with excesss elements tokenGasOverride array provided + gasLimitOverrides[0][0].tokenGasOverrides = new uint32[](3); + vm.expectRevert( + abi.encodeWithSelector(OffRamp.ManualExecutionGasAmountCountMismatch.selector, messages[0].header.messageId, 1) + ); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + } + + function test_manuallyExecute_InvalidTokenGasOverride_Revert() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1000; + amounts[1] = 1000; + Internal.Any2EVMRampMessage[] memory messages = new Internal.Any2EVMRampMessage[](1); + messages[0] = _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + uint32[] memory tokenGasOverrides = new uint32[](2); + tokenGasOverrides[0] = DEFAULT_TOKEN_DEST_GAS_OVERHEAD; + tokenGasOverrides[1] = DEFAULT_TOKEN_DEST_GAS_OVERHEAD - 1; //invalid token gas override value + gasLimitOverrides[0][0].tokenGasOverrides = tokenGasOverrides; + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.InvalidManualExecutionTokenGasOverride.selector, + messages[0].header.messageId, + 1, + DEFAULT_TOKEN_DEST_GAS_OVERHEAD, + tokenGasOverrides[1] + ) + ); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + } + + function test_manuallyExecute_FailedTx_Revert() public { + Internal.Any2EVMRampMessage[] memory messages = + _generateSingleBasicMessage(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1); + + messages[0].receiver = address(s_reverting_receiver); + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + s_offRamp.batchExecute( + _generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), new OffRamp.GasLimitOverride[][](1) + ); + + s_reverting_receiver.setRevert(true); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.ExecutionError.selector, + messages[0].header.messageId, + abi.encodeWithSelector( + OffRamp.ReceiverError.selector, + abi.encodeWithSelector(MaybeRevertMessageReceiver.CustomError.selector, bytes("")) + ) + ) + ); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + } + + function test_manuallyExecute_ReentrancyFails_Success() public { + uint256 tokenAmount = 1e9; + IERC20 tokenToAbuse = IERC20(s_destFeeToken); + + // This needs to be deployed before the source chain message is sent + // because we need the address for the receiver. + ReentrancyAbuserMultiRamp receiver = new ReentrancyAbuserMultiRamp(address(s_destRouter), s_offRamp); + uint256 balancePre = tokenToAbuse.balanceOf(address(receiver)); + + // For this test any message will be flagged as correct by the + // commitStore. In a real scenario the abuser would have to actually + // 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 Internal.Any2EVMTokenTransfer[](1); + messages[0].tokenAmounts[0] = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[s_sourceFeeToken]), + destTokenAddress: s_destTokenBySourceToken[s_sourceFeeToken], + extraData: "", + amount: tokenAmount, + destGasAmount: MAX_TOKEN_POOL_RELEASE_OR_MINT_GAS + }); + + messages[0].receiver = address(receiver); + + messages[0].header.messageId = _hashMessage(messages[0], ON_RAMP_ADDRESS_1); + + Internal.ExecutionReport memory report = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages); + + // sets the report to be repeated on the ReentrancyAbuser to be able to replay + receiver.setPayload(report); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](1); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages); + gasLimitOverrides[0][0].tokenGasOverrides = new uint32[](messages[0].tokenAmounts.length); + + // The first entry should be fine and triggers the second entry which is skipped. Due to the reentrancy + // the second completes first, so we expect the skip event before the success event. + vm.expectEmit(); + emit OffRamp.SkippedAlreadyExecutedMessage( + messages[0].header.sourceChainSelector, messages[0].header.sequenceNumber + ); + + vm.recordLogs(); + s_offRamp.manuallyExecute(_generateBatchReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages), gasLimitOverrides); + _assertExecutionStateChangedEventLogs( + SOURCE_CHAIN_SELECTOR_1, + messages[0].header.sequenceNumber, + messages[0].header.messageId, + _hashMessage(messages[0], ON_RAMP_ADDRESS_1), + Internal.MessageExecutionState.SUCCESS, + "" + ); + + // Since the tx failed we don't release the tokens + assertEq(tokenToAbuse.balanceOf(address(receiver)), balancePre + tokenAmount); + } + + function test_manuallyExecute_MultipleReportsWithSingleCursedLane_Revert() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](3); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](2); + + for (uint64 i = 0; i < 3; ++i) { + messages1[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, i + 1); + messages1[i].receiver = address(s_reverting_receiver); + messages1[i].header.messageId = _hashMessage(messages1[i], ON_RAMP_ADDRESS_1); + } + + for (uint64 i = 0; i < 2; ++i) { + messages2[i] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_3, ON_RAMP_ADDRESS_3, i + 1); + messages2[i].receiver = address(s_reverting_receiver); + messages2[i].header.messageId = _hashMessage(messages2[i], ON_RAMP_ADDRESS_3); + } + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages1); + gasLimitOverrides[1] = _getGasLimitsFromMessages(messages2); + + _setMockRMNChainCurse(SOURCE_CHAIN_SELECTOR_3, true); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.CursedByRMN.selector, SOURCE_CHAIN_SELECTOR_3)); + + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + } + + function test_manuallyExecute_SourceChainSelectorMismatch_Revert() public { + Internal.Any2EVMRampMessage[] memory messages1 = new Internal.Any2EVMRampMessage[](1); + Internal.Any2EVMRampMessage[] memory messages2 = new Internal.Any2EVMRampMessage[](1); + messages1[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + messages2[0] = _generateAny2EVMMessageNoTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1); + + Internal.ExecutionReport[] memory reports = new Internal.ExecutionReport[](2); + reports[0] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_1, messages1); + reports[1] = _generateReportFromMessages(SOURCE_CHAIN_SELECTOR_3, messages2); + + OffRamp.GasLimitOverride[][] memory gasLimitOverrides = new OffRamp.GasLimitOverride[][](2); + gasLimitOverrides[0] = _getGasLimitsFromMessages(messages1); + gasLimitOverrides[1] = _getGasLimitsFromMessages(messages2); + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.SourceChainSelectorMismatch.selector, SOURCE_CHAIN_SELECTOR_3, SOURCE_CHAIN_SELECTOR_1 + ) + ); + s_offRamp.manuallyExecute(reports, gasLimitOverrides); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.releaseOrMintSingleToken.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.releaseOrMintSingleToken.t.sol new file mode 100644 index 00000000000..72999fad42f --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.releaseOrMintSingleToken.t.sol @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {ITokenAdminRegistry} from "../../../interfaces/ITokenAdminRegistry.sol"; + +import {Internal} from "../../../libraries/Internal.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract OffRamp_releaseOrMintSingleToken is OffRampSetup { + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + } + + function test__releaseOrMintSingleToken_Success() public { + uint256 amount = 123123; + address token = s_sourceTokens[0]; + bytes memory originalSender = abi.encode(OWNER); + bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); + + IERC20 dstToken1 = IERC20(s_destTokenBySourceToken[token]); + uint256 startingBalance = dstToken1.balanceOf(OWNER); + + Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), + destTokenAddress: s_destTokenBySourceToken[token], + extraData: "", + amount: amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + vm.expectCall( + s_destPoolBySourceToken[token], + abi.encodeWithSelector( + LockReleaseTokenPool.releaseOrMint.selector, + Pool.ReleaseOrMintInV1({ + originalSender: originalSender, + receiver: OWNER, + amount: amount, + localToken: s_destTokenBySourceToken[token], + remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, + sourcePoolAddress: tokenAmount.sourcePoolAddress, + sourcePoolData: tokenAmount.extraData, + offchainTokenData: offchainTokenData + }) + ) + ); + + s_offRamp.releaseOrMintSingleToken(tokenAmount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData); + + assertEq(startingBalance + amount, dstToken1.balanceOf(OWNER)); + } + + function test_releaseOrMintToken_InvalidDataLength_Revert() public { + uint256 amount = 123123; + address token = s_sourceTokens[0]; + + Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), + destTokenAddress: s_destTokenBySourceToken[token], + extraData: "", + amount: amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + // Mock the call so returns 2 slots of data + vm.mockCall( + s_destTokenBySourceToken[token], abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), abi.encode(0, 0) + ); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.InvalidDataLength.selector, Internal.MAX_BALANCE_OF_RET_BYTES, 64)); + + s_offRamp.releaseOrMintSingleToken(tokenAmount, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR, ""); + } + + function test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() public { + uint256 amount = 123123; + address token = s_sourceTokens[0]; + + Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), + destTokenAddress: s_destTokenBySourceToken[token], + extraData: "", + amount: amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + bytes memory revertData = "failed to balanceOf"; + + // Mock the call so returns 2 slots of data + vm.mockCallRevert( + s_destTokenBySourceToken[token], abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), revertData + ); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, revertData)); + + s_offRamp.releaseOrMintSingleToken(tokenAmount, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR, ""); + } + + function test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() public { + uint256 amount = 123123; + address token = s_sourceTokens[0]; + uint256 mockedStaticBalance = 50000; + + Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), + destTokenAddress: s_destTokenBySourceToken[token], + extraData: "", + amount: amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + vm.mockCall( + s_destTokenBySourceToken[token], + abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), + abi.encode(mockedStaticBalance) + ); + + vm.expectRevert( + abi.encodeWithSelector( + OffRamp.ReleaseOrMintBalanceMismatch.selector, amount, mockedStaticBalance, mockedStaticBalance + ) + ); + + s_offRamp.releaseOrMintSingleToken(tokenAmount, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR, ""); + } + + function test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() public { + uint256 amount = 123123; + address token = s_sourceTokens[0]; + uint256 mockedStaticBalance = 50000; + + Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), + destTokenAddress: s_destTokenBySourceToken[token], + extraData: "", + amount: amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + // This should make the call fail if it does not skip the check + vm.mockCall( + s_destTokenBySourceToken[token], + abi.encodeWithSelector(IERC20.balanceOf.selector, OWNER), + abi.encode(mockedStaticBalance) + ); + + s_offRamp.releaseOrMintSingleToken( + tokenAmount, abi.encode(OWNER), s_destPoolBySourceToken[token], SOURCE_CHAIN_SELECTOR, "" + ); + } + + function test__releaseOrMintSingleToken_NotACompatiblePool_Revert() public { + uint256 amount = 123123; + address token = s_sourceTokens[0]; + address destToken = s_destTokenBySourceToken[token]; + vm.label(destToken, "destToken"); + bytes memory originalSender = abi.encode(OWNER); + bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); + + Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), + destTokenAddress: destToken, + extraData: "", + amount: amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + // Address(0) should always revert + address returnedPool = address(0); + + vm.mockCall( + address(s_tokenAdminRegistry), + abi.encodeWithSelector(ITokenAdminRegistry.getPool.selector, destToken), + abi.encode(returnedPool) + ); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, returnedPool)); + + 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); + + vm.mockCall( + address(s_tokenAdminRegistry), + abi.encodeWithSelector(ITokenAdminRegistry.getPool.selector, destToken), + abi.encode(returnedPool) + ); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, returnedPool)); + + s_offRamp.releaseOrMintSingleToken(tokenAmount, originalSender, OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData); + } + + function test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() public { + address receiver = makeAddr("receiver"); + uint256 amount = 123123; + address token = s_sourceTokens[0]; + address destToken = s_destTokenBySourceToken[token]; + bytes memory originalSender = abi.encode(OWNER); + bytes memory offchainTokenData = abi.encode(keccak256("offchainTokenData")); + + Internal.Any2EVMTokenTransfer memory tokenAmount = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[token]), + destTokenAddress: destToken, + extraData: "", + amount: amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + bytes memory revertData = "call reverted :o"; + + vm.mockCallRevert(destToken, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount), revertData); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, revertData)); + s_offRamp.releaseOrMintSingleToken( + tokenAmount, originalSender, receiver, SOURCE_CHAIN_SELECTOR_1, offchainTokenData + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.releaseOrMintTokens.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.releaseOrMintTokens.t.sol new file mode 100644 index 00000000000..22f82bdf694 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.releaseOrMintTokens.t.sol @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {CallWithExactGas} from "../../../../shared/call/CallWithExactGas.sol"; +import {Client} from "../../../libraries/Client.sol"; +import {Internal} from "../../../libraries/Internal.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; +import {MaybeRevertingBurnMintTokenPool} from "../../helpers/MaybeRevertingBurnMintTokenPool.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract OffRamp_releaseOrMintTokens is OffRampSetup { + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + } + + function test_releaseOrMintTokens_Success() public { + Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); + IERC20 dstToken1 = IERC20(s_destFeeToken); + uint256 startingBalance = dstToken1.balanceOf(OWNER); + uint256 amount1 = 100; + srcTokenAmounts[0].amount = amount1; + + bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); + offchainTokenData[0] = abi.encode(0x12345678); + + Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); + + vm.expectCall( + s_destPoolBySourceToken[srcTokenAmounts[0].token], + abi.encodeWithSelector( + LockReleaseTokenPool.releaseOrMint.selector, + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: OWNER, + amount: srcTokenAmounts[0].amount, + localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], + remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, + offchainTokenData: offchainTokenData[0] + }) + ) + ); + + s_offRamp.releaseOrMintTokens( + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, new uint32[](0) + ); + + assertEq(startingBalance + amount1, dstToken1.balanceOf(OWNER)); + } + + function test_releaseOrMintTokens_WithGasOverride_Success() public { + Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); + IERC20 dstToken1 = IERC20(s_destFeeToken); + uint256 startingBalance = dstToken1.balanceOf(OWNER); + uint256 amount1 = 100; + srcTokenAmounts[0].amount = amount1; + + bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); + offchainTokenData[0] = abi.encode(0x12345678); + + Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); + + vm.expectCall( + s_destPoolBySourceToken[srcTokenAmounts[0].token], + abi.encodeWithSelector( + LockReleaseTokenPool.releaseOrMint.selector, + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: OWNER, + amount: srcTokenAmounts[0].amount, + localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], + remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, + offchainTokenData: offchainTokenData[0] + }) + ) + ); + + uint32[] memory gasOverrides = new uint32[](sourceTokenAmounts.length); + for (uint256 i = 0; i < gasOverrides.length; i++) { + gasOverrides[i] = DEFAULT_TOKEN_DEST_GAS_OVERHEAD + 1; + } + s_offRamp.releaseOrMintTokens( + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, gasOverrides + ); + + assertEq(startingBalance + amount1, dstToken1.balanceOf(OWNER)); + } + + function test_releaseOrMintTokens_destDenominatedDecimals_Success() public { + Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); + uint256 amount = 100; + uint256 destinationDenominationMultiplier = 1000; + srcTokenAmounts[1].amount = amount; + + bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); + + Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); + + address pool = s_destPoolBySourceToken[srcTokenAmounts[1].token]; + address destToken = s_destTokenBySourceToken[srcTokenAmounts[1].token]; + + MaybeRevertingBurnMintTokenPool(pool).setReleaseOrMintMultiplier(destinationDenominationMultiplier); + + Client.EVMTokenAmount[] memory destTokenAmounts = s_offRamp.releaseOrMintTokens( + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, new uint32[](0) + ); + assertEq(destTokenAmounts[1].amount, amount * destinationDenominationMultiplier); + assertEq(destTokenAmounts[1].token, destToken); + } + + // Revert + + function test_TokenHandlingError_Reverts() public { + Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); + + bytes memory unknownError = bytes("unknown error"); + s_maybeRevertingPool.setShouldRevert(unknownError); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, unknownError)); + + s_offRamp.releaseOrMintTokens( + _getDefaultSourceTokenData(srcTokenAmounts), + abi.encode(OWNER), + OWNER, + SOURCE_CHAIN_SELECTOR_1, + new bytes[](srcTokenAmounts.length), + new uint32[](0) + ); + } + + function test_releaseOrMintTokens_InvalidDataLengthReturnData_Revert() public { + uint256 amount = 100; + Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); + srcTokenAmounts[0].amount = amount; + + bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); + Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); + + vm.mockCall( + s_destPoolBySourceToken[srcTokenAmounts[0].token], + abi.encodeWithSelector( + LockReleaseTokenPool.releaseOrMint.selector, + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: OWNER, + amount: amount, + localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], + remoteChainSelector: SOURCE_CHAIN_SELECTOR_1, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, + offchainTokenData: offchainTokenData[0] + }) + ), + // Includes the amount twice, this will revert due to the return data being to long + abi.encode(amount, amount) + ); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.InvalidDataLength.selector, Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES, 64)); + + s_offRamp.releaseOrMintTokens( + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, offchainTokenData, new uint32[](0) + ); + } + + function test__releaseOrMintTokens_PoolIsNotAPool_Reverts() public { + // The offRamp is a contract, but not a pool + address fakePoolAddress = address(s_offRamp); + + Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = new Internal.Any2EVMTokenTransfer[](1); + sourceTokenAmounts[0] = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(fakePoolAddress), + destTokenAddress: address(s_offRamp), + extraData: "", + amount: 1, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + vm.expectRevert(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, address(0))); + s_offRamp.releaseOrMintTokens( + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, new bytes[](1), new uint32[](0) + ); + } + + function test_releaseOrMintTokens_PoolDoesNotSupportDest_Reverts() public { + Client.EVMTokenAmount[] memory srcTokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); + uint256 amount1 = 100; + srcTokenAmounts[0].amount = amount1; + + bytes[] memory offchainTokenData = new bytes[](srcTokenAmounts.length); + offchainTokenData[0] = abi.encode(0x12345678); + + Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = _getDefaultSourceTokenData(srcTokenAmounts); + + vm.expectCall( + s_destPoolBySourceToken[srcTokenAmounts[0].token], + abi.encodeWithSelector( + LockReleaseTokenPool.releaseOrMint.selector, + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: OWNER, + amount: srcTokenAmounts[0].amount, + localToken: s_destTokenBySourceToken[srcTokenAmounts[0].token], + remoteChainSelector: SOURCE_CHAIN_SELECTOR_3, + sourcePoolAddress: sourceTokenAmounts[0].sourcePoolAddress, + sourcePoolData: sourceTokenAmounts[0].extraData, + offchainTokenData: offchainTokenData[0] + }) + ) + ); + vm.expectRevert(); + s_offRamp.releaseOrMintTokens( + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_3, offchainTokenData, new uint32[](0) + ); + } + + /// forge-config: default.fuzz.runs = 32 + /// forge-config: ccip.fuzz.runs = 1024 + // Uint256 gives a good range of values to test, both inside and outside of the eth address space. + function test_Fuzz__releaseOrMintTokens_AnyRevertIsCaught_Success( + address destPool + ) public { + // Input 447301751254033913445893214690834296930546521452, which is 0x4E59B44847B379578588920CA78FBF26C0B4956C + // triggers some Create2Deployer and causes it to fail + vm.assume(destPool != 0x4e59b44847b379578588920cA78FbF26c0B4956C); + bytes memory unusedVar = abi.encode(makeAddr("unused")); + Internal.Any2EVMTokenTransfer[] memory sourceTokenAmounts = new Internal.Any2EVMTokenTransfer[](1); + sourceTokenAmounts[0] = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: unusedVar, + destTokenAddress: destPool, + extraData: unusedVar, + amount: 1, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + try s_offRamp.releaseOrMintTokens( + sourceTokenAmounts, abi.encode(OWNER), OWNER, SOURCE_CHAIN_SELECTOR_1, new bytes[](1), new uint32[](0) + ) {} catch (bytes memory reason) { + // Any revert should be a TokenHandlingError, InvalidEVMAddress, InvalidDataLength or NoContract as those are caught by the offramp + assertTrue( + bytes4(reason) == OffRamp.TokenHandlingError.selector || bytes4(reason) == Internal.InvalidEVMAddress.selector + || bytes4(reason) == OffRamp.InvalidDataLength.selector + || bytes4(reason) == CallWithExactGas.NoContract.selector + || bytes4(reason) == OffRamp.NotACompatiblePool.selector, + "Expected TokenHandlingError or InvalidEVMAddress" + ); + + if (uint160(destPool) > type(uint160).max) { + assertEq(reason, abi.encodeWithSelector(Internal.InvalidEVMAddress.selector, abi.encode(destPool))); + } + } + } + + function _getDefaultSourceTokenData( + Client.EVMTokenAmount[] memory srcTokenAmounts + ) internal view returns (Internal.Any2EVMTokenTransfer[] memory) { + Internal.Any2EVMTokenTransfer[] memory sourceTokenData = new Internal.Any2EVMTokenTransfer[](srcTokenAmounts.length); + for (uint256 i = 0; i < srcTokenAmounts.length; ++i) { + sourceTokenData[i] = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(s_sourcePoolByToken[srcTokenAmounts[i].token]), + destTokenAddress: s_destTokenBySourceToken[srcTokenAmounts[i].token], + extraData: "", + amount: srcTokenAmounts[i].amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + } + return sourceTokenData; + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.setDynamicConfig.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.setDynamicConfig.t.sol new file mode 100644 index 00000000000..384d9b446aa --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.setDynamicConfig.t.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../shared/access/Ownable2Step.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +contract OffRamp_setDynamicConfig is OffRampSetup { + function test_SetDynamicConfig_Success() public { + OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); + + vm.expectEmit(); + emit OffRamp.DynamicConfigSet(dynamicConfig); + + s_offRamp.setDynamicConfig(dynamicConfig); + + OffRamp.DynamicConfig memory newConfig = s_offRamp.getDynamicConfig(); + _assertSameConfig(dynamicConfig, newConfig); + } + + function test_SetDynamicConfigWithInterceptor_Success() public { + OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); + dynamicConfig.messageInterceptor = address(s_inboundMessageInterceptor); + + vm.expectEmit(); + emit OffRamp.DynamicConfigSet(dynamicConfig); + + s_offRamp.setDynamicConfig(dynamicConfig); + + OffRamp.DynamicConfig memory newConfig = s_offRamp.getDynamicConfig(); + _assertSameConfig(dynamicConfig, newConfig); + } + + // Reverts + + function test_NonOwner_Revert() public { + vm.startPrank(STRANGER); + OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(s_feeQuoter)); + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + + s_offRamp.setDynamicConfig(dynamicConfig); + } + + function test_FeeQuoterZeroAddress_Revert() public { + OffRamp.DynamicConfig memory dynamicConfig = _generateDynamicOffRampConfig(address(0)); + + vm.expectRevert(OffRamp.ZeroAddressNotAllowed.selector); + + s_offRamp.setDynamicConfig(dynamicConfig); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.trialExecute.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.trialExecute.t.sol new file mode 100644 index 00000000000..8e944b91ab3 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRamp.trialExecute.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../../libraries/Internal.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {OffRampSetup} from "./OffRampSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract OffRamp_trialExecute is OffRampSetup { + function setUp() public virtual override { + super.setUp(); + _setupMultipleOffRamps(); + } + + function test_trialExecute_Success() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1000; + amounts[1] = 50; + + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + IERC20 dstToken0 = IERC20(s_destTokens[0]); + uint256 startingBalance = dstToken0.balanceOf(message.receiver); + + (Internal.MessageExecutionState newState, bytes memory err) = + s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); + assertEq("", err); + + // Check that the tokens were transferred + assertEq(startingBalance + amounts[0], dstToken0.balanceOf(message.receiver)); + } + + function test_TokenHandlingErrorIsCaught_Success() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1000; + amounts[1] = 50; + + IERC20 dstToken0 = IERC20(s_destTokens[0]); + uint256 startingBalance = dstToken0.balanceOf(OWNER); + + bytes memory errorMessage = "Random token pool issue"; + + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + s_maybeRevertingPool.setShouldRevert(errorMessage); + + (Internal.MessageExecutionState newState, bytes memory err) = + s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); + assertEq(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, errorMessage), err); + + // Expect the balance to remain the same + assertEq(startingBalance, dstToken0.balanceOf(OWNER)); + } + + function test_RateLimitError_Success() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 1000; + amounts[1] = 50; + + bytes memory errorMessage = abi.encodeWithSelector(RateLimiter.BucketOverfilled.selector); + + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + s_maybeRevertingPool.setShouldRevert(errorMessage); + + (Internal.MessageExecutionState newState, bytes memory err) = + s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); + assertEq(abi.encodeWithSelector(OffRamp.TokenHandlingError.selector, errorMessage), err); + } + + // TODO test actual pool exists but isn't compatible instead of just no pool + function test_TokenPoolIsNotAContract_Success() public { + uint256[] memory amounts = new uint256[](2); + amounts[0] = 10000; + Internal.Any2EVMRampMessage memory message = + _generateAny2EVMMessageWithTokens(SOURCE_CHAIN_SELECTOR_1, ON_RAMP_ADDRESS_1, 1, amounts); + + // Happy path, pool is correct + (Internal.MessageExecutionState newState, bytes memory err) = + s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + + assertEq(uint256(Internal.MessageExecutionState.SUCCESS), uint256(newState)); + assertEq("", err); + + // address 0 has no contract + assertEq(address(0).code.length, 0); + + message.tokenAmounts[0] = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(address(0)), + destTokenAddress: address(0), + extraData: "", + amount: message.tokenAmounts[0].amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + message.header.messageId = _hashMessage(message, ON_RAMP_ADDRESS_1); + + // Unhappy path, no revert but marked as failed. + (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + + assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); + assertEq(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, address(0)), err); + + address notAContract = makeAddr("not_a_contract"); + + message.tokenAmounts[0] = Internal.Any2EVMTokenTransfer({ + sourcePoolAddress: abi.encode(address(0)), + destTokenAddress: notAContract, + extraData: "", + amount: message.tokenAmounts[0].amount, + destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD + }); + + message.header.messageId = _hashMessage(message, ON_RAMP_ADDRESS_1); + + (newState, err) = s_offRamp.trialExecute(message, new bytes[](message.tokenAmounts.length), new uint32[](0)); + + assertEq(uint256(Internal.MessageExecutionState.FAILURE), uint256(newState)); + assertEq(abi.encodeWithSelector(OffRamp.NotACompatiblePool.selector, address(0)), err); + } +} diff --git a/contracts/src/v0.8/ccip/test/offRamp/OffRampSetup.t.sol b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRampSetup.t.sol similarity index 82% rename from contracts/src/v0.8/ccip/test/offRamp/OffRampSetup.t.sol rename to contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRampSetup.t.sol index 3b43e1ad0be..68b32390c0a 100644 --- a/contracts/src/v0.8/ccip/test/offRamp/OffRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/offRamp/offRamp/OffRampSetup.t.sol @@ -1,23 +1,22 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {IAny2EVMMessageReceiver} from "../../interfaces/IAny2EVMMessageReceiver.sol"; -import {IRMNRemote} from "../../interfaces/IRMNRemote.sol"; - -import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; -import {NonceManager} from "../../NonceManager.sol"; -import {Router} from "../../Router.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {MultiOCR3Base} from "../../ocr/MultiOCR3Base.sol"; -import {OffRamp} from "../../offRamp/OffRamp.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {FeeQuoterSetup} from "../feeQuoter/FeeQuoterSetup.t.sol"; -import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; -import {MessageInterceptorHelper} from "../helpers/MessageInterceptorHelper.sol"; -import {OffRampHelper} from "../helpers/OffRampHelper.sol"; -import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; -import {MultiOCR3BaseSetup} from "../ocr/MultiOCR3BaseSetup.t.sol"; +import {IAny2EVMMessageReceiver} from "../../../interfaces/IAny2EVMMessageReceiver.sol"; +import {IRMNRemote} from "../../../interfaces/IRMNRemote.sol"; + +import {AuthorizedCallers} from "../../../../shared/access/AuthorizedCallers.sol"; +import {NonceManager} from "../../../NonceManager.sol"; +import {Router} from "../../../Router.sol"; +import {Client} from "../../../libraries/Client.sol"; +import {Internal} from "../../../libraries/Internal.sol"; +import {MultiOCR3Base} from "../../../ocr/MultiOCR3Base.sol"; +import {OffRamp} from "../../../offRamp/OffRamp.sol"; +import {FeeQuoterSetup} from "../../feeQuoter/FeeQuoterSetup.t.sol"; +import {MaybeRevertingBurnMintTokenPool} from "../../helpers/MaybeRevertingBurnMintTokenPool.sol"; +import {MessageInterceptorHelper} from "../../helpers/MessageInterceptorHelper.sol"; +import {OffRampHelper} from "../../helpers/OffRampHelper.sol"; +import {MaybeRevertMessageReceiver} from "../../helpers/receivers/MaybeRevertMessageReceiver.sol"; +import {MultiOCR3BaseSetup} from "../../ocr/MultiOCR3BaseSetup.t.sol"; import {Vm} from "forge-std/Test.sol"; contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { @@ -25,12 +24,12 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { uint64 internal constant SOURCE_CHAIN_SELECTOR_2 = 6433500567565415381; uint64 internal constant SOURCE_CHAIN_SELECTOR_3 = 4051577828743386545; + address internal constant ON_RAMP_ADDRESS = 0x11118e64e1FB0c487f25dD6D3601FF6aF8d32E4e; + bytes internal constant ON_RAMP_ADDRESS_1 = abi.encode(ON_RAMP_ADDRESS); bytes internal constant ON_RAMP_ADDRESS_2 = abi.encode(0xaA3f843Cf8E33B1F02dd28303b6bD87B1aBF8AE4); bytes internal constant ON_RAMP_ADDRESS_3 = abi.encode(0x71830C37Cb193e820de488Da111cfbFcC680a1b9); - address internal constant BLESS_VOTE_ADDR = address(8888); - IAny2EVMMessageReceiver internal s_receiver; IAny2EVMMessageReceiver internal s_secondary_receiver; MaybeRevertMessageReceiver internal s_reverting_receiver; @@ -40,12 +39,10 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { OffRampHelper internal s_offRamp; MessageInterceptorHelper internal s_inboundMessageInterceptor; NonceManager internal s_inboundNonceManager; - address internal s_sourceTokenPool = makeAddr("sourceTokenPool"); bytes32 internal s_configDigestExec; bytes32 internal s_configDigestCommit; - uint64 internal constant s_offchainConfigVersion = 3; - uint8 internal constant s_F = 1; + uint8 internal constant F = 1; uint64 internal s_latestSequenceNumber; @@ -80,14 +77,14 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { sourceChainConfigs ); - s_configDigestExec = _getBasicConfigDigest(s_F, s_emptySigners, s_validTransmitters); - s_configDigestCommit = _getBasicConfigDigest(s_F, s_validSigners, s_validTransmitters); + s_configDigestExec = _getBasicConfigDigest(F, s_emptySigners, s_validTransmitters); + s_configDigestCommit = _getBasicConfigDigest(F, s_validSigners, s_validTransmitters); MultiOCR3Base.OCRConfigArgs[] memory ocrConfigs = new MultiOCR3Base.OCRConfigArgs[](2); ocrConfigs[0] = MultiOCR3Base.OCRConfigArgs({ ocrPluginType: uint8(Internal.OCRPluginType.Execution), configDigest: s_configDigestExec, - F: s_F, + F: F, isSignatureVerificationEnabled: false, signers: s_emptySigners, transmitters: s_validTransmitters @@ -95,7 +92,7 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { ocrConfigs[1] = MultiOCR3Base.OCRConfigArgs({ ocrPluginType: uint8(Internal.OCRPluginType.Commit), configDigest: s_configDigestCommit, - F: s_F, + F: F, isSignatureVerificationEnabled: true, signers: s_validSigners, transmitters: s_validTransmitters @@ -165,44 +162,17 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { s_destRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); } - uint32 internal constant MAX_TOKEN_POOL_RELEASE_OR_MINT_GAS = 200_000; - uint32 internal constant MAX_TOKEN_POOL_TRANSFER_GAS = 50_000; - function _generateDynamicOffRampConfig( address feeQuoter ) internal pure returns (OffRamp.DynamicConfig memory) { return OffRamp.DynamicConfig({ feeQuoter: feeQuoter, - permissionLessExecutionThresholdSeconds: PERMISSION_LESS_EXECUTION_THRESHOLD_SECONDS, + permissionLessExecutionThresholdSeconds: 60 * 60, isRMNVerificationDisabled: false, messageInterceptor: address(0) }); } - function _convertToGeneralMessage( - Internal.Any2EVMRampMessage memory original - ) internal view returns (Client.Any2EVMMessage memory message) { - uint256 numberOfTokens = original.tokenAmounts.length; - Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](numberOfTokens); - - for (uint256 i = 0; i < numberOfTokens; ++i) { - Internal.Any2EVMTokenTransfer memory tokenAmount = original.tokenAmounts[i]; - - address destPoolAddress = tokenAmount.destTokenAddress; - TokenPool pool = TokenPool(destPoolAddress); - destTokenAmounts[i].token = address(pool.getToken()); - destTokenAmounts[i].amount = tokenAmount.amount; - } - - return Client.Any2EVMMessage({ - messageId: original.header.messageId, - sourceChainSelector: original.header.sourceChainSelector, - sender: abi.encode(original.sender), - data: original.data, - destTokenAmounts: destTokenAmounts - }); - } - function _generateAny2EVMMessageNoTokens( uint64 sourceChainSelector, bytes memory onRamp, @@ -290,6 +260,18 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { return messages; } + function _getCastedSourceEVMTokenAmountsWithZeroAmounts() + internal + view + returns (Client.EVMTokenAmount[] memory tokenAmounts) + { + tokenAmounts = new Client.EVMTokenAmount[](s_sourceTokens.length); + for (uint256 i = 0; i < tokenAmounts.length; ++i) { + tokenAmounts[i].token = s_sourceTokens[i]; + } + return tokenAmounts; + } + function _generateReportFromMessages( uint64 sourceChainSelector, Internal.Any2EVMRampMessage[] memory messages @@ -345,22 +327,6 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { assertEq(address(config1.router), address(config2.router)); } - function _getDefaultSourceTokenData( - Client.EVMTokenAmount[] memory srcTokenAmounts - ) internal view returns (Internal.Any2EVMTokenTransfer[] memory) { - Internal.Any2EVMTokenTransfer[] memory sourceTokenData = new Internal.Any2EVMTokenTransfer[](srcTokenAmounts.length); - for (uint256 i = 0; i < srcTokenAmounts.length; ++i) { - sourceTokenData[i] = Internal.Any2EVMTokenTransfer({ - sourcePoolAddress: abi.encode(s_sourcePoolByToken[srcTokenAmounts[i].token]), - destTokenAddress: s_destTokenBySourceToken[srcTokenAmounts[i].token], - extraData: "", - amount: srcTokenAmounts[i].amount, - destGasAmount: DEFAULT_TOKEN_DEST_GAS_OVERHEAD - }); - } - return sourceTokenData; - } - function _enableInboundMessageInterceptor() internal { OffRamp.DynamicConfig memory dynamicConfig = s_offRamp.getDynamicConfig(); dynamicConfig.messageInterceptor = address(s_inboundMessageInterceptor); @@ -397,7 +363,7 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { bytes32[3] memory reportContext = [s_configDigestCommit, bytes32(uint256(sequenceNumber)), s_configDigestCommit]; (bytes32[] memory rs, bytes32[] memory ss,, bytes32 rawVs) = - _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, s_F + 1); + _getSignaturesForDigest(s_validSignerKeys, abi.encode(commitReport), reportContext, F + 1); vm.startPrank(s_validTransmitters[0]); s_offRamp.commit(reportContext, abi.encode(commitReport), rs, ss, rawVs); @@ -412,14 +378,14 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { s_offRamp.execute(reportContext, abi.encode(reports)); } - function assertExecutionStateChangedEventLogs( + function _assertExecutionStateChangedEventLogs( uint64 sourceChainSelector, uint64 sequenceNumber, bytes32 messageId, bytes32 messageHash, Internal.MessageExecutionState state, bytes memory returnData - ) public { + ) internal { Vm.Log[] memory logs = vm.getRecordedLogs(); for (uint256 i = 0; i < logs.length; ++i) { if (logs[i].topics[0] == OffRamp.ExecutionStateChanged.selector) { @@ -440,7 +406,7 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { } } - function assertExecutionStateChangedEventLogs( + function _assertExecutionStateChangedEventLogs( Vm.Log[] memory logs, uint64 sourceChainSelector, uint64 sequenceNumber, @@ -448,7 +414,7 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { bytes32 messageHash, Internal.MessageExecutionState state, bytes memory returnData - ) public pure { + ) internal pure { for (uint256 i = 0; i < logs.length; ++i) { if (logs[i].topics[0] == OffRamp.ExecutionStateChanged.selector) { uint64 logSourceChainSelector = uint64(uint256(logs[i].topics[1])); @@ -473,7 +439,7 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { ) internal { Vm.Log[] memory logs = vm.getRecordedLogs(); - for (uint256 i = 0; i < logs.length; i++) { + for (uint256 i = 0; i < logs.length; ++i) { assertTrue(logs[i].topics[0] != eventSelector); } } @@ -494,4 +460,11 @@ contract OffRampSetup is FeeQuoterSetup, MultiOCR3BaseSetup { ) ); } + + function _getEmptyPriceUpdates() internal pure returns (Internal.PriceUpdates memory priceUpdates) { + return Internal.PriceUpdates({ + tokenPriceUpdates: new Internal.TokenPriceUpdate[](0), + gasPriceUpdates: new Internal.GasPriceUpdate[](0) + }); + } } diff --git a/contracts/src/v0.8/ccip/test/onRamp/OnRamp.t.sol b/contracts/src/v0.8/ccip/test/onRamp/OnRamp.t.sol deleted file mode 100644 index 5aea578f3d9..00000000000 --- a/contracts/src/v0.8/ccip/test/onRamp/OnRamp.t.sol +++ /dev/null @@ -1,1086 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IMessageInterceptor} from "../../interfaces/IMessageInterceptor.sol"; -import {IRMNRemote} from "../../interfaces/IRMNRemote.sol"; -import {IRouter} from "../../interfaces/IRouter.sol"; - -import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {FeeQuoter} from "../../FeeQuoter.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {USDPriceWith18Decimals} from "../../libraries/USDPriceWith18Decimals.sol"; -import {OnRamp} from "../../onRamp/OnRamp.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {MaybeRevertingBurnMintTokenPool} from "../helpers/MaybeRevertingBurnMintTokenPool.sol"; -import "./OnRampSetup.t.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract OnRamp_constructor is OnRampSetup { - function test_Constructor_Success() public { - OnRamp.StaticConfig memory staticConfig = OnRamp.StaticConfig({ - chainSelector: SOURCE_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - nonceManager: address(s_outboundNonceManager), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }); - OnRamp.DynamicConfig memory dynamicConfig = _generateDynamicOnRampConfig(address(s_feeQuoter)); - - vm.expectEmit(); - emit OnRamp.ConfigSet(staticConfig, dynamicConfig); - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); - - _deployOnRamp(SOURCE_CHAIN_SELECTOR, s_sourceRouter, address(s_outboundNonceManager), address(s_tokenAdminRegistry)); - - OnRamp.StaticConfig memory gotStaticConfig = s_onRamp.getStaticConfig(); - _assertStaticConfigsEqual(staticConfig, gotStaticConfig); - - OnRamp.DynamicConfig memory gotDynamicConfig = s_onRamp.getDynamicConfig(); - _assertDynamicConfigsEqual(dynamicConfig, gotDynamicConfig); - - // Initial values - assertEq("OnRamp 1.6.0-dev", s_onRamp.typeAndVersion()); - assertEq(OWNER, s_onRamp.owner()); - assertEq(1, s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR)); - } - - function test_Constructor_EnableAllowList_ForwardFromRouter_Reverts() public { - OnRamp.StaticConfig memory staticConfig = OnRamp.StaticConfig({ - chainSelector: SOURCE_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - nonceManager: address(s_outboundNonceManager), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }); - - OnRamp.DynamicConfig memory dynamicConfig = _generateDynamicOnRampConfig(address(s_feeQuoter)); - - // Creating a DestChainConfig and setting allowlistEnabled : true - OnRamp.DestChainConfigArgs[] memory destChainConfigs = new OnRamp.DestChainConfigArgs[](1); - destChainConfigs[0] = OnRamp.DestChainConfigArgs({ - destChainSelector: DEST_CHAIN_SELECTOR, - router: s_sourceRouter, - allowlistEnabled: true - }); - - vm.expectEmit(); - emit OnRamp.ConfigSet(staticConfig, dynamicConfig); - - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, true); - - OnRampHelper tempOnRamp = new OnRampHelper(staticConfig, dynamicConfig, destChainConfigs); - - // Sending a message and expecting revert as allowlist is enabled with no address in allowlist - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - vm.startPrank(address(s_sourceRouter)); - vm.expectRevert(abi.encodeWithSelector(OnRamp.SenderNotAllowed.selector, OWNER)); - tempOnRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_Constructor_InvalidConfigChainSelectorEqZero_Revert() public { - vm.expectRevert(OnRamp.InvalidConfig.selector); - new OnRampHelper( - OnRamp.StaticConfig({ - chainSelector: 0, - rmnRemote: s_mockRMNRemote, - nonceManager: address(s_outboundNonceManager), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - _generateDynamicOnRampConfig(address(s_feeQuoter)), - _generateDestChainConfigArgs(IRouter(address(0))) - ); - } - - function test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() public { - vm.expectRevert(OnRamp.InvalidConfig.selector); - s_onRamp = new OnRampHelper( - OnRamp.StaticConfig({ - chainSelector: SOURCE_CHAIN_SELECTOR, - rmnRemote: IRMNRemote(address(0)), - nonceManager: address(s_outboundNonceManager), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - _generateDynamicOnRampConfig(address(s_feeQuoter)), - _generateDestChainConfigArgs(IRouter(address(0))) - ); - } - - function test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() public { - vm.expectRevert(OnRamp.InvalidConfig.selector); - new OnRampHelper( - OnRamp.StaticConfig({ - chainSelector: SOURCE_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - nonceManager: address(0), - tokenAdminRegistry: address(s_tokenAdminRegistry) - }), - _generateDynamicOnRampConfig(address(s_feeQuoter)), - _generateDestChainConfigArgs(IRouter(address(0))) - ); - } - - function test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() public { - vm.expectRevert(OnRamp.InvalidConfig.selector); - new OnRampHelper( - OnRamp.StaticConfig({ - chainSelector: SOURCE_CHAIN_SELECTOR, - rmnRemote: s_mockRMNRemote, - nonceManager: address(s_outboundNonceManager), - tokenAdminRegistry: address(0) - }), - _generateDynamicOnRampConfig(address(s_feeQuoter)), - _generateDestChainConfigArgs(IRouter(address(0))) - ); - } -} - -contract OnRamp_forwardFromRouter is OnRampSetup { - struct LegacyExtraArgs { - uint256 gasLimit; - bool strict; - } - - function setUp() public virtual override { - super.setUp(); - - address[] memory feeTokens = new address[](1); - feeTokens[0] = s_sourceTokens[1]; - s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); - - uint64[] memory destinationChainSelectors = new uint64[](1); - destinationChainSelectors[0] = DEST_CHAIN_SELECTOR; - address[] memory addAllowedList = new address[](1); - addAllowedList[0] = OWNER; - OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ - allowlistEnabled: true, - destChainSelector: DEST_CHAIN_SELECTOR, - addedAllowlistedSenders: addAllowedList, - removedAllowlistedSenders: new address[](0) - }); - OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); - applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; - s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); - - // Since we'll mostly be testing for valid calls from the router we'll - // mock all calls to be originating from the router and re-mock in - // tests that require failure. - vm.startPrank(address(s_sourceRouter)); - } - - function test_ForwardFromRouterSuccessCustomExtraArgs() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouter_Success_ConfigurableSourceRouter() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - // Change the source router for this lane - IRouter newRouter = IRouter(makeAddr("NEW ROUTER")); - vm.stopPrank(); - vm.prank(OWNER); - s_onRamp.applyDestChainConfigUpdates(_generateDestChainConfigArgs(newRouter)); - - // forward fails from wrong router - vm.prank(address(s_sourceRouter)); - vm.expectRevert(OnRamp.MustBeCalledByRouter.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - - // forward succeeds from correct router - vm.prank(address(newRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouterSuccessLegacyExtraArgs() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = - abi.encodeWithSelector(Client.EVM_EXTRA_ARGS_V1_TAG, LegacyExtraArgs({gasLimit: GAS_LIMIT * 2, strict: true})); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - // We expect the message to be emitted with strict = false. - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouterSuccessEmptyExtraArgs() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = ""; - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - // We expect the message to be emitted with strict = false. - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouter_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouterExtraArgsV2_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: false}) - ); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: true}) - ); - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_ShouldIncrementSeqNumAndNonce_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - for (uint64 i = 1; i < 4; ++i) { - uint64 nonceBefore = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); - uint64 sequenceNumberBefore = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, i, _messageToEvent(message, i, i, 0, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - uint64 nonceAfter = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); - uint64 sequenceNumberAfter = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; - assertEq(nonceAfter, nonceBefore + 1); - assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); - } - } - - function test_ShouldIncrementNonceOnlyOnOrdered_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = abi.encodeWithSelector( - Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: true}) - ); - - for (uint64 i = 1; i < 4; ++i) { - uint64 nonceBefore = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); - uint64 sequenceNumberBefore = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, i, _messageToEvent(message, i, i, 0, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - uint64 nonceAfter = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); - uint64 sequenceNumberAfter = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; - assertEq(nonceAfter, nonceBefore); - assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); - } - } - - function test_ShouldStoreLinkFees() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint256 feeAmount = 1234567890; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - - assertEq(IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)), feeAmount); - } - - function test_ShouldStoreNonLinkFees() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = s_sourceTokens[1]; - - uint256 feeAmount = 1234567890; - IERC20(s_sourceTokens[1]).transferFrom(OWNER, address(s_onRamp), feeAmount); - - // Calculate conversion done by prices contract - uint256 feeTokenPrice = s_feeQuoter.getTokenPrice(s_sourceTokens[1]).value; - uint256 linkTokenPrice = s_feeQuoter.getTokenPrice(s_sourceFeeToken).value; - uint256 conversionRate = (feeTokenPrice * 1e18) / linkTokenPrice; - uint256 expectedJuels = (feeAmount * conversionRate) / 1e18; - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, expectedJuels, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - - assertEq(IERC20(s_sourceTokens[1]).balanceOf(address(s_onRamp)), feeAmount); - } - - // Make sure any valid sender, receiver and feeAmount can be handled. - // @TODO Temporarily setting lower fuzz run as 256 triggers snapshot gas off by 1 error. - // https://github.com/foundry-rs/foundry/issues/5689 - /// forge-dynamicConfig: default.fuzz.runs = 32 - /// forge-dynamicConfig: ccip.fuzz.runs = 32 - function test_Fuzz_ForwardFromRouter_Success(address originalSender, address receiver, uint96 feeTokenAmount) public { - // To avoid RouterMustSetOriginalSender - vm.assume(originalSender != address(0)); - vm.assume(uint160(receiver) >= Internal.PRECOMPILE_SPACE); - feeTokenAmount = uint96(bound(feeTokenAmount, 0, MAX_MSG_FEES_JUELS)); - vm.stopPrank(); - - vm.startPrank(OWNER); - uint64[] memory destinationChainSelectors = new uint64[](1); - destinationChainSelectors[0] = uint64(DEST_CHAIN_SELECTOR); - address[] memory addAllowedList = new address[](1); - addAllowedList[0] = originalSender; - OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ - allowlistEnabled: true, - destChainSelector: DEST_CHAIN_SELECTOR, - addedAllowlistedSenders: addAllowedList, - removedAllowlistedSenders: new address[](0) - }); - OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); - applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; - s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); - vm.stopPrank(); - - vm.startPrank(address(s_sourceRouter)); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.receiver = abi.encode(receiver); - - // Make sure the tokens are in the contract - deal(s_sourceFeeToken, address(s_onRamp), feeTokenAmount); - - Internal.EVM2AnyRampMessage memory expectedEvent = _messageToEvent(message, 1, 1, feeTokenAmount, originalSender); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, expectedEvent.header.sequenceNumber, expectedEvent); - - // Assert the message Id is correct - assertEq( - expectedEvent.header.messageId, - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeTokenAmount, originalSender) - ); - } - - function test_forwardFromRouter_WithInterception_Success() public { - _enableOutboundMessageInterceptor(); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); - uint256 feeAmount = 1234567890; - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].amount = 1e18; - message.tokenAmounts[0].token = s_sourceTokens[0]; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - s_outboundMessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), false); - - vm.expectEmit(); - emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - // Reverts - - function test_Paused_Revert() public { - // We pause by disabling the whitelist - vm.stopPrank(); - vm.startPrank(OWNER); - s_onRamp.setDynamicConfig(_generateDynamicOnRampConfig(address(2))); - vm.expectRevert(OnRamp.MustBeCalledByRouter.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, OWNER); - } - - function test_InvalidExtraArgsTag_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = bytes("bad args"); - - vm.expectRevert(FeeQuoter.InvalidExtraArgsTag.selector); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_Permissions_Revert() public { - vm.stopPrank(); - vm.startPrank(OWNER); - vm.expectRevert(OnRamp.MustBeCalledByRouter.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, OWNER); - } - - function test_OriginalSender_Revert() public { - vm.expectRevert(OnRamp.RouterMustSetOriginalSender.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, address(0)); - } - - function test_UnAllowedOriginalSender_Revert() public { - vm.stopPrank(); - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(OnRamp.SenderNotAllowed.selector, STRANGER)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, STRANGER); - } - - function test_MessageInterceptionError_Revert() public { - _enableOutboundMessageInterceptor(); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); - uint256 feeAmount = 1234567890; - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].amount = 1e18; - message.tokenAmounts[0].token = s_sourceTokens[0]; - IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); - s_outboundMessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), true); - - vm.expectRevert( - abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) - ); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - - function test_MultiCannotSendZeroTokens_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].amount = 0; - message.tokenAmounts[0].token = s_sourceTokens[0]; - vm.expectRevert(OnRamp.CannotSendZeroTokens.selector); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_UnsupportedToken_Revert() public { - address wrongToken = address(1); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.tokenAmounts = new Client.EVMTokenAmount[](1); - message.tokenAmounts[0].token = wrongToken; - message.tokenAmounts[0].amount = 1; - - // We need to set the price of this new token to be able to reach - // the proper revert point. This must be called by the owner. - vm.stopPrank(); - vm.startPrank(OWNER); - - Internal.PriceUpdates memory priceUpdates = _getSingleTokenPriceUpdateStruct(wrongToken, 1); - s_feeQuoter.updatePrices(priceUpdates); - - // Change back to the router - vm.startPrank(address(s_sourceRouter)); - vm.expectRevert(abi.encodeWithSelector(OnRamp.UnsupportedToken.selector, wrongToken)); - - 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); - message.tokenAmounts[0].amount = 1; - message.tokenAmounts[0].token = address(1); - - vm.expectRevert(abi.encodeWithSelector(OnRamp.UnsupportedToken.selector, message.tokenAmounts[0].token)); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } - - function test_MesssageFeeTooHigh_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - vm.expectRevert( - abi.encodeWithSelector(FeeQuoter.MessageFeeTooHigh.selector, MAX_MSG_FEES_JUELS + 1, MAX_MSG_FEES_JUELS) - ); - - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, MAX_MSG_FEES_JUELS + 1, OWNER); - } - - function test_SourceTokenDataTooLarge_Revert() public { - address sourceETH = s_sourceTokens[1]; - vm.stopPrank(); - vm.startPrank(OWNER); - - MaybeRevertingBurnMintTokenPool newPool = new MaybeRevertingBurnMintTokenPool( - BurnMintERC677(sourceETH), new address[](0), address(s_mockRMNRemote), 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)); - - // 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: abi.encode(s_destToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - newPool.applyChainUpdates(chainUpdates); - - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(address(sourceETH), 1000); - - // No data set, should succeed - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set max data length, should succeed - vm.startPrank(OWNER); - newPool.setSourceTokenData(new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES)); - - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set data to max length +1, should revert - vm.startPrank(OWNER); - newPool.setSourceTokenData(new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + 1)); - - vm.startPrank(address(s_sourceRouter)); - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set token config to allow larger data - vm.startPrank(OWNER); - FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); - tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = sourceETH; - tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ - minFeeUSDCents: 0, - maxFeeUSDCents: 1, - deciBps: 0, - destGasOverhead: 0, - destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, - isEnabled: true - }); - s_feeQuoter.applyTokenTransferFeeConfigUpdates( - tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) - ); - - vm.startPrank(address(s_sourceRouter)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - - // Set the token data larger than the configured token data, should revert - vm.startPrank(OWNER); - newPool.setSourceTokenData(new bytes(uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32 + 1)); - - vm.startPrank(address(s_sourceRouter)); - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); - } -} - -contract OnRamp_getSupportedTokens is OnRampSetup { - function test_GetSupportedTokens_Revert() public { - vm.expectRevert(OnRamp.GetSupportedTokensFunctionalityRemovedCheckAdminRegistry.selector); - s_onRamp.getSupportedTokens(DEST_CHAIN_SELECTOR); - } -} - -contract OnRamp_getFee is OnRampSetup { - using USDPriceWith18Decimals for uint224; - - function test_EmptyMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = testTokens[i]; - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - uint256 expectedFeeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - - assertEq(expectedFeeAmount, feeAmount); - } - } - - function test_SingleTokenMessage_Success() public view { - address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; - uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; - - uint256 tokenAmount = 10000e18; - for (uint256 i = 0; i < feeTokenPrices.length; ++i) { - Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); - message.feeToken = testTokens[i]; - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - uint256 expectedFeeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); - - assertEq(expectedFeeAmount, feeAmount); - } - } - - function test_GetFeeOfZeroForTokenMessage_Success() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - - uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - assertTrue(feeAmount > 0); - - FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory tokenMults = new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](1); - tokenMults[0] = FeeQuoter.PremiumMultiplierWeiPerEthArgs({token: message.feeToken, premiumMultiplierWeiPerEth: 0}); - s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(tokenMults); - - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = 0; - destChainConfigArgs[0].destChainConfig.gasMultiplierWeiPerEth = 0; - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - - feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - - assertEq(0, feeAmount); - } - - // Reverts - - function test_Unhealthy_Revert() public { - _setMockRMNChainCurse(DEST_CHAIN_SELECTOR, true); - vm.expectRevert(abi.encodeWithSelector(OnRamp.CursedByRMN.selector, DEST_CHAIN_SELECTOR)); - s_onRamp.getFee(DEST_CHAIN_SELECTOR, _generateEmptyMessage()); - } - - function test_EnforceOutOfOrder_Revert() public { - // Update dynamic config to enforce allowOutOfOrderExecution = true. - vm.stopPrank(); - vm.startPrank(OWNER); - - FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); - destChainConfigArgs[0].destChainConfig.enforceOutOfOrder = true; - s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); - vm.stopPrank(); - - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - // Empty extraArgs to should revert since it enforceOutOfOrder is true. - message.extraArgs = ""; - - vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); - s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - } - - function test_NotAFeeTokenButPricedToken_Revert() public { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = s_sourceTokens[1]; - - vm.expectRevert(abi.encodeWithSelector(FeeQuoter.FeeTokenNotSupported.selector, message.feeToken)); - - s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); - } -} - -contract OnRamp_setDynamicConfig is OnRampSetup { - function test_setDynamicConfig_Success() public { - OnRamp.StaticConfig memory staticConfig = s_onRamp.getStaticConfig(); - OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ - feeQuoter: address(23423), - reentrancyGuardEntered: false, - messageInterceptor: makeAddr("messageInterceptor"), - feeAggregator: FEE_AGGREGATOR, - allowlistAdmin: address(0) - }); - - vm.expectEmit(); - emit OnRamp.ConfigSet(staticConfig, newConfig); - - s_onRamp.setDynamicConfig(newConfig); - - OnRamp.DynamicConfig memory gotDynamicConfig = s_onRamp.getDynamicConfig(); - assertEq(newConfig.feeQuoter, gotDynamicConfig.feeQuoter); - } - - // Reverts - - function test_setDynamicConfig_InvalidConfigFeeQuoterEqAddressZero_Revert() public { - OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ - feeQuoter: address(0), - reentrancyGuardEntered: false, - feeAggregator: FEE_AGGREGATOR, - messageInterceptor: makeAddr("messageInterceptor"), - allowlistAdmin: address(0) - }); - - vm.expectRevert(OnRamp.InvalidConfig.selector); - s_onRamp.setDynamicConfig(newConfig); - } - - function test_setDynamicConfig_InvalidConfigInvalidConfig_Revert() public { - OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ - feeQuoter: address(23423), - reentrancyGuardEntered: false, - messageInterceptor: address(0), - feeAggregator: FEE_AGGREGATOR, - allowlistAdmin: address(0) - }); - - // Invalid price reg reverts. - newConfig.feeQuoter = address(0); - vm.expectRevert(OnRamp.InvalidConfig.selector); - s_onRamp.setDynamicConfig(newConfig); - } - - function test_setDynamicConfig_InvalidConfigFeeAggregatorEqAddressZero_Revert() public { - OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ - feeQuoter: address(23423), - reentrancyGuardEntered: false, - messageInterceptor: address(0), - feeAggregator: address(0), - allowlistAdmin: address(0) - }); - - vm.expectRevert(OnRamp.InvalidConfig.selector); - s_onRamp.setDynamicConfig(newConfig); - } - - function test_setDynamicConfig_InvalidConfigOnlyOwner_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - s_onRamp.setDynamicConfig(_generateDynamicOnRampConfig(address(2))); - vm.startPrank(ADMIN); - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - s_onRamp.setDynamicConfig(_generateDynamicOnRampConfig(address(2))); - } - - function test_setDynamicConfig_InvalidConfigReentrancyGuardEnteredEqTrue_Revert() public { - OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ - feeQuoter: address(23423), - reentrancyGuardEntered: true, - messageInterceptor: makeAddr("messageInterceptor"), - feeAggregator: FEE_AGGREGATOR, - allowlistAdmin: address(0) - }); - - vm.expectRevert(OnRamp.InvalidConfig.selector); - s_onRamp.setDynamicConfig(newConfig); - } -} - -contract OnRamp_withdrawFeeTokens is OnRampSetup { - mapping(address => uint256) internal s_nopFees; - - function setUp() public virtual override { - super.setUp(); - - // Since we'll mostly be testing for valid calls from the router we'll - // mock all calls to be originating from the router and re-mock in - // tests that require failure. - vm.startPrank(address(s_sourceRouter)); - - uint256 feeAmount = 1234567890; - - // Send a bunch of messages, increasing the juels in the contract - for (uint256 i = 0; i < s_sourceFeeTokens.length; ++i) { - Client.EVM2AnyMessage memory message = _generateEmptyMessage(); - message.feeToken = s_sourceFeeTokens[i % s_sourceFeeTokens.length]; - uint256 newFeeTokenBalance = IERC20(message.feeToken).balanceOf(address(s_onRamp)) + feeAmount; - deal(message.feeToken, address(s_onRamp), newFeeTokenBalance); - s_nopFees[message.feeToken] = newFeeTokenBalance; - s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); - } - } - - function test_Fuzz_WithdrawFeeTokens_Success( - uint256[5] memory amounts - ) public { - vm.startPrank(OWNER); - address[] memory feeTokens = new address[](amounts.length); - for (uint256 i = 0; i < amounts.length; ++i) { - vm.assume(amounts[i] > 0); - feeTokens[i] = _deploySourceToken("", amounts[i], 18); - IERC20(feeTokens[i]).transfer(address(s_onRamp), amounts[i]); - } - - s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); - - for (uint256 i = 0; i < feeTokens.length; ++i) { - vm.expectEmit(); - emit OnRamp.FeeTokenWithdrawn(FEE_AGGREGATOR, feeTokens[i], amounts[i]); - } - - s_onRamp.withdrawFeeTokens(feeTokens); - - for (uint256 i = 0; i < feeTokens.length; ++i) { - assertEq(IERC20(feeTokens[i]).balanceOf(FEE_AGGREGATOR), amounts[i]); - assertEq(IERC20(feeTokens[i]).balanceOf(address(s_onRamp)), 0); - } - } - - function test_WithdrawFeeTokens_Success() public { - vm.expectEmit(); - emit OnRamp.FeeTokenWithdrawn(FEE_AGGREGATOR, s_sourceFeeToken, s_nopFees[s_sourceFeeToken]); - - s_onRamp.withdrawFeeTokens(s_sourceFeeTokens); - - assertEq(IERC20(s_sourceFeeToken).balanceOf(FEE_AGGREGATOR), s_nopFees[s_sourceFeeToken]); - assertEq(IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)), 0); - } -} - -contract OnRamp_getTokenPool is OnRampSetup { - function test_GetTokenPool_Success() public view { - assertEq( - s_sourcePoolByToken[s_sourceTokens[0]], - address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(s_sourceTokens[0]))) - ); - assertEq( - s_sourcePoolByToken[s_sourceTokens[1]], - address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(s_sourceTokens[1]))) - ); - - address wrongToken = address(123); - address nonExistentPool = address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(wrongToken))); - - assertEq(address(0), nonExistentPool); - } -} - -contract OnRamp_applyDestChainConfigUpdates is OnRampSetup { - function test_ApplyDestChainConfigUpdates_Success() external { - OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](1); - configArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; - - // supports disabling a lane by setting a router to zero - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, IRouter(address(0)), false); - - s_onRamp.applyDestChainConfigUpdates(configArgs); - - (,, address router) = s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR); - assertEq(address(0), router); - - // supports updating and adding lanes simultaneously - configArgs = new OnRamp.DestChainConfigArgs[](2); - configArgs[0] = OnRamp.DestChainConfigArgs({ - destChainSelector: DEST_CHAIN_SELECTOR, - router: s_sourceRouter, - allowlistEnabled: false - }); - uint64 newDestChainSelector = 99999; - address newRouter = makeAddr("newRouter"); - - configArgs[1] = OnRamp.DestChainConfigArgs({ - destChainSelector: newDestChainSelector, - router: IRouter(newRouter), - allowlistEnabled: false - }); - - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(newDestChainSelector, 0, IRouter(newRouter), false); - - s_onRamp.applyDestChainConfigUpdates(configArgs); - - (,, address newGotRouter) = s_onRamp.getDestChainConfig(newDestChainSelector); - assertEq(newRouter, newGotRouter); - - // handles empty list - uint256 numLogs = vm.getRecordedLogs().length; - configArgs = new OnRamp.DestChainConfigArgs[](0); - s_onRamp.applyDestChainConfigUpdates(configArgs); - assertEq(numLogs, vm.getRecordedLogs().length); // indicates no changes made - } - - function test_ApplyDestChainConfigUpdates_WithInvalidChainSelector_Revert() external { - OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](1); - configArgs[0].destChainSelector = 0; // invalid - vm.expectRevert(abi.encodeWithSelector(OnRamp.InvalidDestChainConfig.selector, 0)); - s_onRamp.applyDestChainConfigUpdates(configArgs); - } -} - -contract OnRamp_applyAllowlistUpdates is OnRampSetup { - function test_applyAllowlistUpdates_Success() public { - OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](2); - configArgs[0] = OnRamp.DestChainConfigArgs({ - destChainSelector: DEST_CHAIN_SELECTOR, - router: s_sourceRouter, - allowlistEnabled: false - }); - configArgs[1] = - OnRamp.DestChainConfigArgs({destChainSelector: 9999, router: IRouter(address(9999)), allowlistEnabled: false}); - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(9999, 0, IRouter(address(9999)), false); - s_onRamp.applyDestChainConfigUpdates(configArgs); - - (uint64 sequenceNumber, bool allowlistEnabled, address router) = s_onRamp.getDestChainConfig(9999); - assertEq(sequenceNumber, 0); - assertEq(allowlistEnabled, false); - assertEq(router, address(9999)); - - uint64[] memory destinationChainSelectors = new uint64[](2); - destinationChainSelectors[0] = DEST_CHAIN_SELECTOR; - destinationChainSelectors[1] = uint64(99999); - - address[] memory addedAllowlistedSenders = new address[](4); - addedAllowlistedSenders[0] = vm.addr(1); - addedAllowlistedSenders[1] = vm.addr(2); - addedAllowlistedSenders[2] = vm.addr(3); - addedAllowlistedSenders[3] = vm.addr(4); - - vm.expectEmit(); - emit OnRamp.AllowListSendersAdded(DEST_CHAIN_SELECTOR, addedAllowlistedSenders); - - OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ - allowlistEnabled: true, - destChainSelector: DEST_CHAIN_SELECTOR, - addedAllowlistedSenders: addedAllowlistedSenders, - removedAllowlistedSenders: new address[](0) - }); - - OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); - applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; - - s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); - - (bool isActive, address[] memory gotAllowList) = s_onRamp.getAllowedSendersList(DEST_CHAIN_SELECTOR); - assertEq(4, gotAllowList.length); - assertEq(addedAllowlistedSenders, gotAllowList); - assertEq(true, isActive); - - address[] memory removedAllowlistedSenders = new address[](1); - removedAllowlistedSenders[0] = vm.addr(2); - - vm.expectEmit(); - emit OnRamp.AllowListSendersRemoved(DEST_CHAIN_SELECTOR, removedAllowlistedSenders); - - allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ - allowlistEnabled: false, - destChainSelector: DEST_CHAIN_SELECTOR, - addedAllowlistedSenders: new address[](0), - removedAllowlistedSenders: removedAllowlistedSenders - }); - - OnRamp.AllowlistConfigArgs[] memory allowlistConfigArgsItems_2 = new OnRamp.AllowlistConfigArgs[](1); - allowlistConfigArgsItems_2[0] = allowlistConfigArgs; - - s_onRamp.applyAllowlistUpdates(allowlistConfigArgsItems_2); - (isActive, gotAllowList) = s_onRamp.getAllowedSendersList(DEST_CHAIN_SELECTOR); - assertEq(3, gotAllowList.length); - assertFalse(isActive); - - addedAllowlistedSenders = new address[](2); - addedAllowlistedSenders[0] = vm.addr(5); - addedAllowlistedSenders[1] = vm.addr(6); - - removedAllowlistedSenders = new address[](2); - removedAllowlistedSenders[0] = vm.addr(1); - removedAllowlistedSenders[1] = vm.addr(3); - - vm.expectEmit(); - emit OnRamp.AllowListSendersAdded(DEST_CHAIN_SELECTOR, addedAllowlistedSenders); - emit OnRamp.AllowListSendersRemoved(DEST_CHAIN_SELECTOR, removedAllowlistedSenders); - - allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ - allowlistEnabled: true, - destChainSelector: DEST_CHAIN_SELECTOR, - addedAllowlistedSenders: addedAllowlistedSenders, - removedAllowlistedSenders: removedAllowlistedSenders - }); - - OnRamp.AllowlistConfigArgs[] memory allowlistConfigArgsItems_3 = new OnRamp.AllowlistConfigArgs[](1); - allowlistConfigArgsItems_3[0] = allowlistConfigArgs; - - s_onRamp.applyAllowlistUpdates(allowlistConfigArgsItems_3); - (isActive, gotAllowList) = s_onRamp.getAllowedSendersList(DEST_CHAIN_SELECTOR); - - assertEq(3, gotAllowList.length); - assertTrue(isActive); - } - - function test_applyAllowlistUpdates_Revert() public { - OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](2); - configArgs[0] = OnRamp.DestChainConfigArgs({ - destChainSelector: DEST_CHAIN_SELECTOR, - router: s_sourceRouter, - allowlistEnabled: false - }); - configArgs[1] = - OnRamp.DestChainConfigArgs({destChainSelector: 9999, router: IRouter(address(9999)), allowlistEnabled: false}); - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); - vm.expectEmit(); - emit OnRamp.DestChainConfigSet(9999, 0, IRouter(address(9999)), false); - s_onRamp.applyDestChainConfigUpdates(configArgs); - - uint64[] memory destinationChainSelectors = new uint64[](2); - destinationChainSelectors[0] = DEST_CHAIN_SELECTOR; - destinationChainSelectors[1] = uint64(99999); - - address[] memory addedAllowlistedSenders = new address[](4); - addedAllowlistedSenders[0] = vm.addr(1); - addedAllowlistedSenders[1] = vm.addr(2); - addedAllowlistedSenders[2] = vm.addr(3); - addedAllowlistedSenders[3] = vm.addr(4); - - OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ - allowlistEnabled: true, - destChainSelector: DEST_CHAIN_SELECTOR, - addedAllowlistedSenders: addedAllowlistedSenders, - removedAllowlistedSenders: new address[](0) - }); - - OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); - applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; - - vm.startPrank(STRANGER); - vm.expectRevert(OnRamp.OnlyCallableByOwnerOrAllowlistAdmin.selector); - s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); - vm.stopPrank(); - - applyAllowlistConfigArgsItems[0].addedAllowlistedSenders[0] = address(0); - vm.expectRevert(abi.encodeWithSelector(OnRamp.InvalidAllowListRequest.selector, DEST_CHAIN_SELECTOR)); - vm.startPrank(OWNER); - s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); - vm.stopPrank(); - } - - function test_applyAllowlistUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() public { - address[] memory addedAllowlistedSenders = new address[](1); - addedAllowlistedSenders[0] = vm.addr(1); - - OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ - allowlistEnabled: false, - destChainSelector: DEST_CHAIN_SELECTOR, - addedAllowlistedSenders: addedAllowlistedSenders, - removedAllowlistedSenders: new address[](0) - }); - OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); - applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; - - vm.expectRevert(abi.encodeWithSelector(OnRamp.InvalidAllowListRequest.selector, DEST_CHAIN_SELECTOR)); - s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); - } -} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.applyDestChainConfigUpdates.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.applyDestChainConfigUpdates.t.sol new file mode 100644 index 00000000000..2b99fd423be --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.applyDestChainConfigUpdates.t.sol @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IRouter} from "../../../interfaces/IRouter.sol"; + +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +contract OnRamp_applyDestChainConfigUpdates is OnRampSetup { + function test_ApplyDestChainConfigUpdates_Success() external { + OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](1); + configArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + + // supports disabling a lane by setting a router to zero + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, IRouter(address(0)), false); + + s_onRamp.applyDestChainConfigUpdates(configArgs); + + (,, address router) = s_onRamp.getDestChainConfig(DEST_CHAIN_SELECTOR); + assertEq(address(0), router); + + // supports updating and adding lanes simultaneously + configArgs = new OnRamp.DestChainConfigArgs[](2); + configArgs[0] = OnRamp.DestChainConfigArgs({ + destChainSelector: DEST_CHAIN_SELECTOR, + router: s_sourceRouter, + allowlistEnabled: false + }); + uint64 newDestChainSelector = 99999; + address newRouter = makeAddr("newRouter"); + + configArgs[1] = OnRamp.DestChainConfigArgs({ + destChainSelector: newDestChainSelector, + router: IRouter(newRouter), + allowlistEnabled: false + }); + + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(newDestChainSelector, 0, IRouter(newRouter), false); + + s_onRamp.applyDestChainConfigUpdates(configArgs); + + (,, address newGotRouter) = s_onRamp.getDestChainConfig(newDestChainSelector); + assertEq(newRouter, newGotRouter); + + // handles empty list + uint256 numLogs = vm.getRecordedLogs().length; + configArgs = new OnRamp.DestChainConfigArgs[](0); + s_onRamp.applyDestChainConfigUpdates(configArgs); + assertEq(numLogs, vm.getRecordedLogs().length); // indicates no changes made + } + + function test_ApplyDestChainConfigUpdates_WithInvalidChainSelector_Revert() external { + OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](1); + configArgs[0].destChainSelector = 0; // invalid + vm.expectRevert(abi.encodeWithSelector(OnRamp.InvalidDestChainConfig.selector, 0)); + s_onRamp.applyDestChainConfigUpdates(configArgs); + } +} + +contract OnRamp_applyAllowlistUpdates is OnRampSetup { + function test_applyAllowlistUpdates_Success() public { + OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](2); + configArgs[0] = OnRamp.DestChainConfigArgs({ + destChainSelector: DEST_CHAIN_SELECTOR, + router: s_sourceRouter, + allowlistEnabled: false + }); + configArgs[1] = + OnRamp.DestChainConfigArgs({destChainSelector: 9999, router: IRouter(address(9999)), allowlistEnabled: false}); + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(9999, 0, IRouter(address(9999)), false); + s_onRamp.applyDestChainConfigUpdates(configArgs); + + (uint64 sequenceNumber, bool allowlistEnabled, address router) = s_onRamp.getDestChainConfig(9999); + assertEq(sequenceNumber, 0); + assertEq(allowlistEnabled, false); + assertEq(router, address(9999)); + + uint64[] memory destinationChainSelectors = new uint64[](2); + destinationChainSelectors[0] = DEST_CHAIN_SELECTOR; + destinationChainSelectors[1] = uint64(99999); + + address[] memory addedAllowlistedSenders = new address[](4); + addedAllowlistedSenders[0] = vm.addr(1); + addedAllowlistedSenders[1] = vm.addr(2); + addedAllowlistedSenders[2] = vm.addr(3); + addedAllowlistedSenders[3] = vm.addr(4); + + vm.expectEmit(); + emit OnRamp.AllowListSendersAdded(DEST_CHAIN_SELECTOR, addedAllowlistedSenders); + + OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ + allowlistEnabled: true, + destChainSelector: DEST_CHAIN_SELECTOR, + addedAllowlistedSenders: addedAllowlistedSenders, + removedAllowlistedSenders: new address[](0) + }); + + OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); + applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; + + s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); + + (bool isActive, address[] memory gotAllowList) = s_onRamp.getAllowedSendersList(DEST_CHAIN_SELECTOR); + assertEq(4, gotAllowList.length); + assertEq(addedAllowlistedSenders, gotAllowList); + assertEq(true, isActive); + + address[] memory removedAllowlistedSenders = new address[](1); + removedAllowlistedSenders[0] = vm.addr(2); + + vm.expectEmit(); + emit OnRamp.AllowListSendersRemoved(DEST_CHAIN_SELECTOR, removedAllowlistedSenders); + + allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ + allowlistEnabled: false, + destChainSelector: DEST_CHAIN_SELECTOR, + addedAllowlistedSenders: new address[](0), + removedAllowlistedSenders: removedAllowlistedSenders + }); + + OnRamp.AllowlistConfigArgs[] memory allowlistConfigArgsItems_2 = new OnRamp.AllowlistConfigArgs[](1); + allowlistConfigArgsItems_2[0] = allowlistConfigArgs; + + s_onRamp.applyAllowlistUpdates(allowlistConfigArgsItems_2); + (isActive, gotAllowList) = s_onRamp.getAllowedSendersList(DEST_CHAIN_SELECTOR); + assertEq(3, gotAllowList.length); + assertFalse(isActive); + + addedAllowlistedSenders = new address[](2); + addedAllowlistedSenders[0] = vm.addr(5); + addedAllowlistedSenders[1] = vm.addr(6); + + removedAllowlistedSenders = new address[](2); + removedAllowlistedSenders[0] = vm.addr(1); + removedAllowlistedSenders[1] = vm.addr(3); + + vm.expectEmit(); + emit OnRamp.AllowListSendersAdded(DEST_CHAIN_SELECTOR, addedAllowlistedSenders); + emit OnRamp.AllowListSendersRemoved(DEST_CHAIN_SELECTOR, removedAllowlistedSenders); + + allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ + allowlistEnabled: true, + destChainSelector: DEST_CHAIN_SELECTOR, + addedAllowlistedSenders: addedAllowlistedSenders, + removedAllowlistedSenders: removedAllowlistedSenders + }); + + OnRamp.AllowlistConfigArgs[] memory allowlistConfigArgsItems_3 = new OnRamp.AllowlistConfigArgs[](1); + allowlistConfigArgsItems_3[0] = allowlistConfigArgs; + + s_onRamp.applyAllowlistUpdates(allowlistConfigArgsItems_3); + (isActive, gotAllowList) = s_onRamp.getAllowedSendersList(DEST_CHAIN_SELECTOR); + + assertEq(3, gotAllowList.length); + assertTrue(isActive); + } + + function test_applyAllowlistUpdates_Revert() public { + OnRamp.DestChainConfigArgs[] memory configArgs = new OnRamp.DestChainConfigArgs[](2); + configArgs[0] = OnRamp.DestChainConfigArgs({ + destChainSelector: DEST_CHAIN_SELECTOR, + router: s_sourceRouter, + allowlistEnabled: false + }); + configArgs[1] = + OnRamp.DestChainConfigArgs({destChainSelector: 9999, router: IRouter(address(9999)), allowlistEnabled: false}); + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(9999, 0, IRouter(address(9999)), false); + s_onRamp.applyDestChainConfigUpdates(configArgs); + + uint64[] memory destinationChainSelectors = new uint64[](2); + destinationChainSelectors[0] = DEST_CHAIN_SELECTOR; + destinationChainSelectors[1] = uint64(99999); + + address[] memory addedAllowlistedSenders = new address[](4); + addedAllowlistedSenders[0] = vm.addr(1); + addedAllowlistedSenders[1] = vm.addr(2); + addedAllowlistedSenders[2] = vm.addr(3); + addedAllowlistedSenders[3] = vm.addr(4); + + OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ + allowlistEnabled: true, + destChainSelector: DEST_CHAIN_SELECTOR, + addedAllowlistedSenders: addedAllowlistedSenders, + removedAllowlistedSenders: new address[](0) + }); + + OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); + applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; + + vm.startPrank(STRANGER); + vm.expectRevert(OnRamp.OnlyCallableByOwnerOrAllowlistAdmin.selector); + s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); + vm.stopPrank(); + + applyAllowlistConfigArgsItems[0].addedAllowlistedSenders[0] = address(0); + vm.expectRevert(abi.encodeWithSelector(OnRamp.InvalidAllowListRequest.selector, DEST_CHAIN_SELECTOR)); + vm.startPrank(OWNER); + s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); + vm.stopPrank(); + } + + function test_applyAllowlistUpdates_InvalidAllowListRequestDisabledAllowListWithAdds() public { + address[] memory addedAllowlistedSenders = new address[](1); + addedAllowlistedSenders[0] = vm.addr(1); + + OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ + allowlistEnabled: false, + destChainSelector: DEST_CHAIN_SELECTOR, + addedAllowlistedSenders: addedAllowlistedSenders, + removedAllowlistedSenders: new address[](0) + }); + OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); + applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; + + vm.expectRevert(abi.encodeWithSelector(OnRamp.InvalidAllowListRequest.selector, DEST_CHAIN_SELECTOR)); + s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.constructor.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.constructor.t.sol new file mode 100644 index 00000000000..1e31a2a1377 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.constructor.t.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IRMNRemote} from "../../../interfaces/IRMNRemote.sol"; +import {IRouter} from "../../../interfaces/IRouter.sol"; + +import {Client} from "../../../libraries/Client.sol"; +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {OnRampHelper} from "../../helpers/OnRampHelper.sol"; +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +contract OnRamp_constructor is OnRampSetup { + function test_Constructor_Success() public { + OnRamp.StaticConfig memory staticConfig = OnRamp.StaticConfig({ + chainSelector: SOURCE_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + nonceManager: address(s_outboundNonceManager), + tokenAdminRegistry: address(s_tokenAdminRegistry) + }); + OnRamp.DynamicConfig memory dynamicConfig = _generateDynamicOnRampConfig(address(s_feeQuoter)); + + vm.expectEmit(); + emit OnRamp.ConfigSet(staticConfig, dynamicConfig); + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, false); + + _deployOnRamp(SOURCE_CHAIN_SELECTOR, s_sourceRouter, address(s_outboundNonceManager), address(s_tokenAdminRegistry)); + + OnRamp.StaticConfig memory gotStaticConfig = s_onRamp.getStaticConfig(); + + assertEq(staticConfig.chainSelector, gotStaticConfig.chainSelector); + assertEq(address(staticConfig.rmnRemote), address(gotStaticConfig.rmnRemote)); + assertEq(staticConfig.tokenAdminRegistry, gotStaticConfig.tokenAdminRegistry); + + OnRamp.DynamicConfig memory gotDynamicConfig = s_onRamp.getDynamicConfig(); + assertEq(dynamicConfig.feeQuoter, gotDynamicConfig.feeQuoter); + + // Initial values + assertEq("OnRamp 1.6.0-dev", s_onRamp.typeAndVersion()); + assertEq(OWNER, s_onRamp.owner()); + assertEq(1, s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR)); + } + + function test_Constructor_EnableAllowList_ForwardFromRouter_Reverts() public { + OnRamp.StaticConfig memory staticConfig = OnRamp.StaticConfig({ + chainSelector: SOURCE_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + nonceManager: address(s_outboundNonceManager), + tokenAdminRegistry: address(s_tokenAdminRegistry) + }); + + OnRamp.DynamicConfig memory dynamicConfig = _generateDynamicOnRampConfig(address(s_feeQuoter)); + + // Creating a DestChainConfig and setting allowlistEnabled : true + OnRamp.DestChainConfigArgs[] memory destChainConfigs = new OnRamp.DestChainConfigArgs[](1); + destChainConfigs[0] = OnRamp.DestChainConfigArgs({ + destChainSelector: DEST_CHAIN_SELECTOR, + router: s_sourceRouter, + allowlistEnabled: true + }); + + vm.expectEmit(); + emit OnRamp.ConfigSet(staticConfig, dynamicConfig); + + vm.expectEmit(); + emit OnRamp.DestChainConfigSet(DEST_CHAIN_SELECTOR, 0, s_sourceRouter, true); + + OnRampHelper tempOnRamp = new OnRampHelper(staticConfig, dynamicConfig, destChainConfigs); + + // Sending a message and expecting revert as allowlist is enabled with no address in allowlist + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + vm.startPrank(address(s_sourceRouter)); + vm.expectRevert(abi.encodeWithSelector(OnRamp.SenderNotAllowed.selector, OWNER)); + tempOnRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + } + + function test_Constructor_InvalidConfigChainSelectorEqZero_Revert() public { + vm.expectRevert(OnRamp.InvalidConfig.selector); + new OnRampHelper( + OnRamp.StaticConfig({ + chainSelector: 0, + rmnRemote: s_mockRMNRemote, + nonceManager: address(s_outboundNonceManager), + tokenAdminRegistry: address(s_tokenAdminRegistry) + }), + _generateDynamicOnRampConfig(address(s_feeQuoter)), + _generateDestChainConfigArgs(IRouter(address(0))) + ); + } + + function test_Constructor_InvalidConfigRMNProxyEqAddressZero_Revert() public { + vm.expectRevert(OnRamp.InvalidConfig.selector); + s_onRamp = new OnRampHelper( + OnRamp.StaticConfig({ + chainSelector: SOURCE_CHAIN_SELECTOR, + rmnRemote: IRMNRemote(address(0)), + nonceManager: address(s_outboundNonceManager), + tokenAdminRegistry: address(s_tokenAdminRegistry) + }), + _generateDynamicOnRampConfig(address(s_feeQuoter)), + _generateDestChainConfigArgs(IRouter(address(0))) + ); + } + + function test_Constructor_InvalidConfigNonceManagerEqAddressZero_Revert() public { + vm.expectRevert(OnRamp.InvalidConfig.selector); + new OnRampHelper( + OnRamp.StaticConfig({ + chainSelector: SOURCE_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + nonceManager: address(0), + tokenAdminRegistry: address(s_tokenAdminRegistry) + }), + _generateDynamicOnRampConfig(address(s_feeQuoter)), + _generateDestChainConfigArgs(IRouter(address(0))) + ); + } + + function test_Constructor_InvalidConfigTokenAdminRegistryEqAddressZero_Revert() public { + vm.expectRevert(OnRamp.InvalidConfig.selector); + new OnRampHelper( + OnRamp.StaticConfig({ + chainSelector: SOURCE_CHAIN_SELECTOR, + rmnRemote: s_mockRMNRemote, + nonceManager: address(s_outboundNonceManager), + tokenAdminRegistry: address(0) + }), + _generateDynamicOnRampConfig(address(s_feeQuoter)), + _generateDestChainConfigArgs(IRouter(address(0))) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.forwardFromRouter.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.forwardFromRouter.t.sol new file mode 100644 index 00000000000..076377c34c5 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.forwardFromRouter.t.sol @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IMessageInterceptor} from "../../../interfaces/IMessageInterceptor.sol"; +import {IRouter} from "../../../interfaces/IRouter.sol"; + +import {BurnMintERC677} from "../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {FeeQuoter} from "../../../FeeQuoter.sol"; +import {Client} from "../../../libraries/Client.sol"; +import {Internal} from "../../../libraries/Internal.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {MaybeRevertingBurnMintTokenPool} from "../../helpers/MaybeRevertingBurnMintTokenPool.sol"; +import {MessageInterceptorHelper} from "../../helpers/MessageInterceptorHelper.sol"; +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract OnRamp_forwardFromRouter is OnRampSetup { + struct LegacyExtraArgs { + uint256 gasLimit; + bool strict; + } + + MessageInterceptorHelper internal s_outboundMessageInterceptor; + + address internal s_destTokenPool = makeAddr("destTokenPool"); + address internal s_destToken = makeAddr("destToken"); + + function setUp() public virtual override { + super.setUp(); + s_outboundMessageInterceptor = new MessageInterceptorHelper(); + + address[] memory feeTokens = new address[](1); + feeTokens[0] = s_sourceTokens[1]; + s_feeQuoter.applyFeeTokensUpdates(feeTokens, new address[](0)); + + uint64[] memory destinationChainSelectors = new uint64[](1); + destinationChainSelectors[0] = DEST_CHAIN_SELECTOR; + address[] memory addAllowedList = new address[](1); + addAllowedList[0] = OWNER; + OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ + allowlistEnabled: true, + destChainSelector: DEST_CHAIN_SELECTOR, + addedAllowlistedSenders: addAllowedList, + removedAllowlistedSenders: new address[](0) + }); + OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); + applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; + s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); + + // Since we'll mostly be testing for valid calls from the router we'll + // mock all calls to be originating from the router and re-mock in + // tests that require failure. + vm.startPrank(address(s_sourceRouter)); + } + + function test_ForwardFromRouterSuccessCustomExtraArgs() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_ForwardFromRouter_Success_ConfigurableSourceRouter() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + // Change the source router for this lane + IRouter newRouter = IRouter(makeAddr("NEW ROUTER")); + vm.stopPrank(); + vm.prank(OWNER); + s_onRamp.applyDestChainConfigUpdates(_generateDestChainConfigArgs(newRouter)); + + // forward fails from wrong router + vm.prank(address(s_sourceRouter)); + vm.expectRevert(OnRamp.MustBeCalledByRouter.selector); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + + // forward succeeds from correct router + vm.prank(address(newRouter)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_ForwardFromRouterSuccessLegacyExtraArgs() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = + abi.encodeWithSelector(Client.EVM_EXTRA_ARGS_V1_TAG, LegacyExtraArgs({gasLimit: GAS_LIMIT * 2, strict: true})); + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + vm.expectEmit(); + // We expect the message to be emitted with strict = false. + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_ForwardFromRouterSuccessEmptyExtraArgs() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = ""; + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + vm.expectEmit(); + // We expect the message to be emitted with strict = false. + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_ForwardFromRouter_Success() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_ForwardFromRouterExtraArgsV2_Success() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = abi.encodeWithSelector( + Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: false}) + ); + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_ForwardFromRouterExtraArgsV2AllowOutOfOrderTrue_Success() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = abi.encodeWithSelector( + Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: true}) + ); + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_ShouldIncrementSeqNumAndNonce_Success() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + + for (uint64 i = 1; i < 4; ++i) { + uint64 nonceBefore = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 sequenceNumberBefore = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, i, _messageToEvent(message, i, i, 0, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + + uint64 nonceAfter = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 sequenceNumberAfter = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; + assertEq(nonceAfter, nonceBefore + 1); + assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); + } + } + + function test_ShouldIncrementNonceOnlyOnOrdered_Success() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = abi.encodeWithSelector( + Client.EVM_EXTRA_ARGS_V2_TAG, Client.EVMExtraArgsV2({gasLimit: GAS_LIMIT * 2, allowOutOfOrderExecution: true}) + ); + + for (uint64 i = 1; i < 4; ++i) { + uint64 nonceBefore = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 sequenceNumberBefore = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, i, _messageToEvent(message, i, i, 0, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + + uint64 nonceAfter = s_outboundNonceManager.getOutboundNonce(DEST_CHAIN_SELECTOR, OWNER); + uint64 sequenceNumberAfter = s_onRamp.getExpectedNextSequenceNumber(DEST_CHAIN_SELECTOR) - 1; + assertEq(nonceAfter, nonceBefore); + assertEq(sequenceNumberAfter, sequenceNumberBefore + 1); + } + } + + function test_ShouldStoreLinkFees() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + + uint256 feeAmount = 1234567890; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + + assertEq(IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)), feeAmount); + } + + function test_ShouldStoreNonLinkFees() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.feeToken = s_sourceTokens[1]; + + uint256 feeAmount = 1234567890; + IERC20(s_sourceTokens[1]).transferFrom(OWNER, address(s_onRamp), feeAmount); + + // Calculate conversion done by prices contract + uint256 feeTokenPrice = s_feeQuoter.getTokenPrice(s_sourceTokens[1]).value; + uint256 linkTokenPrice = s_feeQuoter.getTokenPrice(s_sourceFeeToken).value; + uint256 conversionRate = (feeTokenPrice * 1e18) / linkTokenPrice; + uint256 expectedJuels = (feeAmount * conversionRate) / 1e18; + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, expectedJuels, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + + assertEq(IERC20(s_sourceTokens[1]).balanceOf(address(s_onRamp)), feeAmount); + } + + // Make sure any valid sender, receiver and feeAmount can be handled. + // @TODO Temporarily setting lower fuzz run as 256 triggers snapshot gas off by 1 error. + // https://github.com/foundry-rs/foundry/issues/5689 + /// forge-dynamicConfig: default.fuzz.runs = 32 + /// forge-dynamicConfig: ccip.fuzz.runs = 32 + function test_Fuzz_ForwardFromRouter_Success(address originalSender, address receiver, uint96 feeTokenAmount) public { + // To avoid RouterMustSetOriginalSender + vm.assume(originalSender != address(0)); + vm.assume(uint160(receiver) >= Internal.PRECOMPILE_SPACE); + feeTokenAmount = uint96(bound(feeTokenAmount, 0, MAX_MSG_FEES_JUELS)); + vm.stopPrank(); + + vm.startPrank(OWNER); + uint64[] memory destinationChainSelectors = new uint64[](1); + destinationChainSelectors[0] = uint64(DEST_CHAIN_SELECTOR); + address[] memory addAllowedList = new address[](1); + addAllowedList[0] = originalSender; + OnRamp.AllowlistConfigArgs memory allowlistConfigArgs = OnRamp.AllowlistConfigArgs({ + allowlistEnabled: true, + destChainSelector: DEST_CHAIN_SELECTOR, + addedAllowlistedSenders: addAllowedList, + removedAllowlistedSenders: new address[](0) + }); + OnRamp.AllowlistConfigArgs[] memory applyAllowlistConfigArgsItems = new OnRamp.AllowlistConfigArgs[](1); + applyAllowlistConfigArgsItems[0] = allowlistConfigArgs; + s_onRamp.applyAllowlistUpdates(applyAllowlistConfigArgsItems); + vm.stopPrank(); + + vm.startPrank(address(s_sourceRouter)); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.receiver = abi.encode(receiver); + + // Make sure the tokens are in the contract + deal(s_sourceFeeToken, address(s_onRamp), feeTokenAmount); + + Internal.EVM2AnyRampMessage memory expectedEvent = _messageToEvent(message, 1, 1, feeTokenAmount, originalSender); + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, expectedEvent.header.sequenceNumber, expectedEvent); + + // Assert the message Id is correct + assertEq( + expectedEvent.header.messageId, + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeTokenAmount, originalSender) + ); + } + + function test_forwardFromRouter_WithInterception_Success() public { + _enableOutboundMessageInterceptor(); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); + uint256 feeAmount = 1234567890; + message.tokenAmounts = new Client.EVMTokenAmount[](1); + message.tokenAmounts[0].amount = 1e18; + message.tokenAmounts[0].token = s_sourceTokens[0]; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + s_outboundMessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), false); + + vm.expectEmit(); + emit OnRamp.CCIPMessageSent(DEST_CHAIN_SELECTOR, 1, _messageToEvent(message, 1, 1, feeAmount, OWNER)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + // Reverts + + function test_Paused_Revert() public { + // We pause by disabling the whitelist + vm.stopPrank(); + vm.startPrank(OWNER); + s_onRamp.setDynamicConfig(_generateDynamicOnRampConfig(address(2))); + vm.expectRevert(OnRamp.MustBeCalledByRouter.selector); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, OWNER); + } + + function test_InvalidExtraArgsTag_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = bytes("bad args"); + + vm.expectRevert(FeeQuoter.InvalidExtraArgsTag.selector); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + } + + function test_Permissions_Revert() public { + vm.stopPrank(); + vm.startPrank(OWNER); + vm.expectRevert(OnRamp.MustBeCalledByRouter.selector); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, OWNER); + } + + function test_OriginalSender_Revert() public { + vm.expectRevert(OnRamp.RouterMustSetOriginalSender.selector); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, address(0)); + } + + function test_UnAllowedOriginalSender_Revert() public { + vm.stopPrank(); + vm.startPrank(STRANGER); + vm.expectRevert(abi.encodeWithSelector(OnRamp.SenderNotAllowed.selector, STRANGER)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, _generateEmptyMessage(), 0, STRANGER); + } + + function test_MessageInterceptionError_Revert() public { + _enableOutboundMessageInterceptor(); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.extraArgs = Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT * 2})); + uint256 feeAmount = 1234567890; + message.tokenAmounts = new Client.EVMTokenAmount[](1); + message.tokenAmounts[0].amount = 1e18; + message.tokenAmounts[0].token = s_sourceTokens[0]; + IERC20(s_sourceFeeToken).transferFrom(OWNER, address(s_onRamp), feeAmount); + s_outboundMessageInterceptor.setMessageIdValidationState(keccak256(abi.encode(message)), true); + + vm.expectRevert( + abi.encodeWithSelector(IMessageInterceptor.MessageValidationError.selector, bytes("Invalid message")) + ); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + + function test_MultiCannotSendZeroTokens_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.tokenAmounts = new Client.EVMTokenAmount[](1); + message.tokenAmounts[0].amount = 0; + message.tokenAmounts[0].token = s_sourceTokens[0]; + vm.expectRevert(OnRamp.CannotSendZeroTokens.selector); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + } + + function test_UnsupportedToken_Revert() public { + address wrongToken = address(1); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.tokenAmounts = new Client.EVMTokenAmount[](1); + message.tokenAmounts[0].token = wrongToken; + message.tokenAmounts[0].amount = 1; + + // We need to set the price of this new token to be able to reach + // the proper revert point. This must be called by the owner. + vm.stopPrank(); + vm.startPrank(OWNER); + + Internal.PriceUpdates memory priceUpdates = _getSingleTokenPriceUpdateStruct(wrongToken, 1); + s_feeQuoter.updatePrices(priceUpdates); + + // Change back to the router + vm.startPrank(address(s_sourceRouter)); + vm.expectRevert(abi.encodeWithSelector(OnRamp.UnsupportedToken.selector, wrongToken)); + + 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); + message.tokenAmounts[0].amount = 1; + message.tokenAmounts[0].token = address(1); + + vm.expectRevert(abi.encodeWithSelector(OnRamp.UnsupportedToken.selector, message.tokenAmounts[0].token)); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + } + + function test_MesssageFeeTooHigh_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + + vm.expectRevert( + abi.encodeWithSelector(FeeQuoter.MessageFeeTooHigh.selector, MAX_MSG_FEES_JUELS + 1, MAX_MSG_FEES_JUELS) + ); + + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, MAX_MSG_FEES_JUELS + 1, OWNER); + } + + function test_SourceTokenDataTooLarge_Revert() public { + address sourceETH = s_sourceTokens[1]; + vm.stopPrank(); + vm.startPrank(OWNER); + + MaybeRevertingBurnMintTokenPool newPool = new MaybeRevertingBurnMintTokenPool( + BurnMintERC677(sourceETH), new address[](0), address(s_mockRMNRemote), 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)); + + // 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: abi.encode(s_destToken), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + newPool.applyChainUpdates(chainUpdates); + + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(address(sourceETH), 1000); + + // No data set, should succeed + vm.startPrank(address(s_sourceRouter)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + + // Set max data length, should succeed + vm.startPrank(OWNER); + newPool.setSourceTokenData(new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES)); + + vm.startPrank(address(s_sourceRouter)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + + // Set data to max length +1, should revert + vm.startPrank(OWNER); + newPool.setSourceTokenData(new bytes(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + 1)); + + vm.startPrank(address(s_sourceRouter)); + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + + // Set token config to allow larger data + vm.startPrank(OWNER); + FeeQuoter.TokenTransferFeeConfigArgs[] memory tokenTransferFeeConfigArgs = _generateTokenTransferFeeConfigArgs(1, 1); + tokenTransferFeeConfigArgs[0].destChainSelector = DEST_CHAIN_SELECTOR; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].token = sourceETH; + tokenTransferFeeConfigArgs[0].tokenTransferFeeConfigs[0].tokenTransferFeeConfig = FeeQuoter.TokenTransferFeeConfig({ + minFeeUSDCents: 0, + maxFeeUSDCents: 1, + deciBps: 0, + destGasOverhead: 0, + destBytesOverhead: uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32, + isEnabled: true + }); + s_feeQuoter.applyTokenTransferFeeConfigUpdates( + tokenTransferFeeConfigArgs, new FeeQuoter.TokenTransferFeeConfigRemoveArgs[](0) + ); + + vm.startPrank(address(s_sourceRouter)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + + // Set the token data larger than the configured token data, should revert + vm.startPrank(OWNER); + newPool.setSourceTokenData(new bytes(uint32(Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES) + 32 + 1)); + + vm.startPrank(address(s_sourceRouter)); + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.SourceTokenDataTooLarge.selector, sourceETH)); + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, 0, OWNER); + } + + function _enableOutboundMessageInterceptor() internal { + (, address msgSender,) = vm.readCallers(); + + bool resetPrank = false; + + if (msgSender != OWNER) { + vm.stopPrank(); + vm.startPrank(OWNER); + resetPrank = true; + } + + OnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); + dynamicConfig.messageInterceptor = address(s_outboundMessageInterceptor); + s_onRamp.setDynamicConfig(dynamicConfig); + + if (resetPrank) { + vm.stopPrank(); + vm.startPrank(msgSender); + } + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getFee.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getFee.t.sol new file mode 100644 index 00000000000..63a4c0c322e --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getFee.t.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {FeeQuoter} from "../../../FeeQuoter.sol"; +import {Client} from "../../../libraries/Client.sol"; +import {USDPriceWith18Decimals} from "../../../libraries/USDPriceWith18Decimals.sol"; +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +contract OnRamp_getFee is OnRampSetup { + using USDPriceWith18Decimals for uint224; + + function test_EmptyMessage_Success() public view { + address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; + uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; + + for (uint256 i = 0; i < feeTokenPrices.length; ++i) { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.feeToken = testTokens[i]; + + uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); + uint256 expectedFeeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + + assertEq(expectedFeeAmount, feeAmount); + } + } + + function test_SingleTokenMessage_Success() public view { + address[2] memory testTokens = [s_sourceFeeToken, s_sourceRouter.getWrappedNative()]; + uint224[2] memory feeTokenPrices = [s_feeTokenPrice, s_wrappedTokenPrice]; + + uint256 tokenAmount = 10000e18; + for (uint256 i = 0; i < feeTokenPrices.length; ++i) { + Client.EVM2AnyMessage memory message = _generateSingleTokenMessage(s_sourceFeeToken, tokenAmount); + message.feeToken = testTokens[i]; + + uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); + uint256 expectedFeeAmount = s_feeQuoter.getValidatedFee(DEST_CHAIN_SELECTOR, message); + + assertEq(expectedFeeAmount, feeAmount); + } + } + + function test_GetFeeOfZeroForTokenMessage_Success() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + + uint256 feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); + assertTrue(feeAmount > 0); + + FeeQuoter.PremiumMultiplierWeiPerEthArgs[] memory tokenMults = new FeeQuoter.PremiumMultiplierWeiPerEthArgs[](1); + tokenMults[0] = FeeQuoter.PremiumMultiplierWeiPerEthArgs({token: message.feeToken, premiumMultiplierWeiPerEth: 0}); + s_feeQuoter.applyPremiumMultiplierWeiPerEthUpdates(tokenMults); + + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + destChainConfigArgs[0].destChainConfig.destDataAvailabilityMultiplierBps = 0; + destChainConfigArgs[0].destChainConfig.gasMultiplierWeiPerEth = 0; + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + + feeAmount = s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); + + assertEq(0, feeAmount); + } + + // Reverts + + function test_Unhealthy_Revert() public { + _setMockRMNChainCurse(DEST_CHAIN_SELECTOR, true); + vm.expectRevert(abi.encodeWithSelector(OnRamp.CursedByRMN.selector, DEST_CHAIN_SELECTOR)); + s_onRamp.getFee(DEST_CHAIN_SELECTOR, _generateEmptyMessage()); + } + + function test_EnforceOutOfOrder_Revert() public { + // Update dynamic config to enforce allowOutOfOrderExecution = true. + vm.stopPrank(); + vm.startPrank(OWNER); + + FeeQuoter.DestChainConfigArgs[] memory destChainConfigArgs = _generateFeeQuoterDestChainConfigArgs(); + destChainConfigArgs[0].destChainConfig.enforceOutOfOrder = true; + s_feeQuoter.applyDestChainConfigUpdates(destChainConfigArgs); + vm.stopPrank(); + + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + // Empty extraArgs to should revert since it enforceOutOfOrder is true. + message.extraArgs = ""; + + vm.expectRevert(FeeQuoter.ExtraArgOutOfOrderExecutionMustBeTrue.selector); + s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); + } + + function test_NotAFeeTokenButPricedToken_Revert() public { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.feeToken = s_sourceTokens[1]; + + vm.expectRevert(abi.encodeWithSelector(FeeQuoter.FeeTokenNotSupported.selector, message.feeToken)); + + s_onRamp.getFee(DEST_CHAIN_SELECTOR, message); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getSupportedTokens.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getSupportedTokens.t.sol new file mode 100644 index 00000000000..c04f3cf3d51 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getSupportedTokens.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +contract OnRamp_getSupportedTokens is OnRampSetup { + function test_GetSupportedTokens_Revert() public { + vm.expectRevert(OnRamp.GetSupportedTokensFunctionalityRemovedCheckAdminRegistry.selector); + s_onRamp.getSupportedTokens(DEST_CHAIN_SELECTOR); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getTokenPool.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getTokenPool.t.sol new file mode 100644 index 00000000000..8612ce86e36 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.getTokenPool.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract OnRamp_getTokenPool is OnRampSetup { + function test_GetTokenPool_Success() public view { + assertEq( + s_sourcePoolByToken[s_sourceTokens[0]], + address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(s_sourceTokens[0]))) + ); + assertEq( + s_sourcePoolByToken[s_sourceTokens[1]], + address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(s_sourceTokens[1]))) + ); + + address wrongToken = address(123); + address nonExistentPool = address(s_onRamp.getPoolBySourceToken(DEST_CHAIN_SELECTOR, IERC20(wrongToken))); + + assertEq(address(0), nonExistentPool); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.setDynamicConfig.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.setDynamicConfig.t.sol new file mode 100644 index 00000000000..057ed0a79dd --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.setDynamicConfig.t.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../shared/access/Ownable2Step.sol"; +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +contract OnRamp_setDynamicConfig is OnRampSetup { + function test_setDynamicConfig_Success() public { + OnRamp.StaticConfig memory staticConfig = s_onRamp.getStaticConfig(); + OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ + feeQuoter: address(23423), + reentrancyGuardEntered: false, + messageInterceptor: makeAddr("messageInterceptor"), + feeAggregator: FEE_AGGREGATOR, + allowlistAdmin: address(0) + }); + + vm.expectEmit(); + emit OnRamp.ConfigSet(staticConfig, newConfig); + + s_onRamp.setDynamicConfig(newConfig); + + OnRamp.DynamicConfig memory gotDynamicConfig = s_onRamp.getDynamicConfig(); + assertEq(newConfig.feeQuoter, gotDynamicConfig.feeQuoter); + } + + // Reverts + + function test_setDynamicConfig_InvalidConfigFeeQuoterEqAddressZero_Revert() public { + OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ + feeQuoter: address(0), + reentrancyGuardEntered: false, + feeAggregator: FEE_AGGREGATOR, + messageInterceptor: makeAddr("messageInterceptor"), + allowlistAdmin: address(0) + }); + + vm.expectRevert(OnRamp.InvalidConfig.selector); + s_onRamp.setDynamicConfig(newConfig); + } + + function test_setDynamicConfig_InvalidConfigInvalidConfig_Revert() public { + OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ + feeQuoter: address(23423), + reentrancyGuardEntered: false, + messageInterceptor: address(0), + feeAggregator: FEE_AGGREGATOR, + allowlistAdmin: address(0) + }); + + // Invalid price reg reverts. + newConfig.feeQuoter = address(0); + vm.expectRevert(OnRamp.InvalidConfig.selector); + s_onRamp.setDynamicConfig(newConfig); + } + + function test_setDynamicConfig_InvalidConfigFeeAggregatorEqAddressZero_Revert() public { + OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ + feeQuoter: address(23423), + reentrancyGuardEntered: false, + messageInterceptor: address(0), + feeAggregator: address(0), + allowlistAdmin: address(0) + }); + + vm.expectRevert(OnRamp.InvalidConfig.selector); + s_onRamp.setDynamicConfig(newConfig); + } + + function test_setDynamicConfig_InvalidConfigOnlyOwner_Revert() public { + vm.startPrank(STRANGER); + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + s_onRamp.setDynamicConfig(_generateDynamicOnRampConfig(address(2))); + } + + function test_setDynamicConfig_InvalidConfigReentrancyGuardEnteredEqTrue_Revert() public { + OnRamp.DynamicConfig memory newConfig = OnRamp.DynamicConfig({ + feeQuoter: address(23423), + reentrancyGuardEntered: true, + messageInterceptor: makeAddr("messageInterceptor"), + feeAggregator: FEE_AGGREGATOR, + allowlistAdmin: address(0) + }); + + vm.expectRevert(OnRamp.InvalidConfig.selector); + s_onRamp.setDynamicConfig(newConfig); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.withdrawFeeTokens.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.withdrawFeeTokens.t.sol new file mode 100644 index 00000000000..d4a297c103c --- /dev/null +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRamp.withdrawFeeTokens.t.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Client} from "../../../libraries/Client.sol"; +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {OnRampSetup} from "./OnRampSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract OnRamp_withdrawFeeTokens is OnRampSetup { + mapping(address => uint256) internal s_nopFees; + + function setUp() public virtual override { + super.setUp(); + + // Since we'll mostly be testing for valid calls from the router we'll + // mock all calls to be originating from the router and re-mock in + // tests that require failure. + vm.startPrank(address(s_sourceRouter)); + + uint256 feeAmount = 1234567890; + + // Send a bunch of messages, increasing the juels in the contract + for (uint256 i = 0; i < s_sourceFeeTokens.length; ++i) { + Client.EVM2AnyMessage memory message = _generateEmptyMessage(); + message.feeToken = s_sourceFeeTokens[i % s_sourceFeeTokens.length]; + uint256 newFeeTokenBalance = IERC20(message.feeToken).balanceOf(address(s_onRamp)) + feeAmount; + deal(message.feeToken, address(s_onRamp), newFeeTokenBalance); + s_nopFees[message.feeToken] = newFeeTokenBalance; + s_onRamp.forwardFromRouter(DEST_CHAIN_SELECTOR, message, feeAmount, OWNER); + } + } + + function test_Fuzz_WithdrawFeeTokens_Success( + uint256[5] memory amounts + ) public { + vm.startPrank(OWNER); + address[] memory feeTokens = new address[](amounts.length); + for (uint256 i = 0; i < amounts.length; ++i) { + vm.assume(amounts[i] > 0); + feeTokens[i] = _deploySourceToken("", amounts[i], 18); + IERC20(feeTokens[i]).transfer(address(s_onRamp), amounts[i]); + } + + s_feeQuoter.applyFeeTokensUpdates(new address[](0), feeTokens); + + for (uint256 i = 0; i < feeTokens.length; ++i) { + vm.expectEmit(); + emit OnRamp.FeeTokenWithdrawn(FEE_AGGREGATOR, feeTokens[i], amounts[i]); + } + + s_onRamp.withdrawFeeTokens(feeTokens); + + for (uint256 i = 0; i < feeTokens.length; ++i) { + assertEq(IERC20(feeTokens[i]).balanceOf(FEE_AGGREGATOR), amounts[i]); + assertEq(IERC20(feeTokens[i]).balanceOf(address(s_onRamp)), 0); + } + } + + function test_WithdrawFeeTokens_Success() public { + vm.expectEmit(); + emit OnRamp.FeeTokenWithdrawn(FEE_AGGREGATOR, s_sourceFeeToken, s_nopFees[s_sourceFeeToken]); + + s_onRamp.withdrawFeeTokens(s_sourceFeeTokens); + + assertEq(IERC20(s_sourceFeeToken).balanceOf(FEE_AGGREGATOR), s_nopFees[s_sourceFeeToken]); + assertEq(IERC20(s_sourceFeeToken).balanceOf(address(s_onRamp)), 0); + } +} diff --git a/contracts/src/v0.8/ccip/test/onRamp/OnRampSetup.t.sol b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRampSetup.t.sol similarity index 58% rename from contracts/src/v0.8/ccip/test/onRamp/OnRampSetup.t.sol rename to contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRampSetup.t.sol index 8254dd977f7..ead9e7088ce 100644 --- a/contracts/src/v0.8/ccip/test/onRamp/OnRampSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/onRamp/onRamp/OnRampSetup.t.sol @@ -1,48 +1,42 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {IRouter} from "../../interfaces/IRouter.sol"; +import {IRouter} from "../../../interfaces/IRouter.sol"; -import {AuthorizedCallers} from "../../../shared/access/AuthorizedCallers.sol"; -import {NonceManager} from "../../NonceManager.sol"; -import {Router} from "../../Router.sol"; -import {Client} from "../../libraries/Client.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {OnRamp} from "../../onRamp/OnRamp.sol"; -import {FeeQuoterFeeSetup} from "../feeQuoter/FeeQuoterSetup.t.sol"; -import {MessageInterceptorHelper} from "../helpers/MessageInterceptorHelper.sol"; -import {OnRampHelper} from "../helpers/OnRampHelper.sol"; +import {AuthorizedCallers} from "../../../../shared/access/AuthorizedCallers.sol"; +import {NonceManager} from "../../../NonceManager.sol"; +import {Router} from "../../../Router.sol"; +import {Client} from "../../../libraries/Client.sol"; +import {Internal} from "../../../libraries/Internal.sol"; +import {OnRamp} from "../../../onRamp/OnRamp.sol"; +import {TokenAdminRegistry} from "../../../tokenAdminRegistry/TokenAdminRegistry.sol"; +import {FeeQuoterFeeSetup} from "../../feeQuoter/FeeQuoterSetup.t.sol"; +import {OnRampHelper} from "../../helpers/OnRampHelper.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; contract OnRampSetup is FeeQuoterFeeSetup { - uint256 internal immutable i_tokenAmount0 = 9; - uint256 internal immutable i_tokenAmount1 = 7; + address internal constant FEE_AGGREGATOR = 0xa33CDB32eAEce34F6affEfF4899cef45744EDea3; bytes32 internal s_metadataHash; OnRampHelper internal s_onRamp; - MessageInterceptorHelper internal s_outboundMessageInterceptor; - address[] internal s_offRamps; NonceManager internal s_outboundNonceManager; function setUp() public virtual override { super.setUp(); - s_outboundMessageInterceptor = new MessageInterceptorHelper(); s_outboundNonceManager = new NonceManager(new address[](0)); (s_onRamp, s_metadataHash) = _deployOnRamp( SOURCE_CHAIN_SELECTOR, s_sourceRouter, address(s_outboundNonceManager), address(s_tokenAdminRegistry) ); - s_offRamps = new address[](2); - s_offRamps[0] = address(10); - s_offRamps[1] = address(11); Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](2); onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: address(s_onRamp)}); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: s_offRamps[0]}); - offRampUpdates[1] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: s_offRamps[1]}); + + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](2); + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: makeAddr("offRamp0")}); + offRampUpdates[1] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: makeAddr("offRamp1")}); s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); // Pre approve the first token so the gas estimates of the tests @@ -51,19 +45,6 @@ contract OnRampSetup is FeeQuoterFeeSetup { IERC20(s_sourceTokens[1]).approve(address(s_sourceRouter), 2 ** 128); } - function _generateTokenMessage() public view returns (Client.EVM2AnyMessage memory) { - Client.EVMTokenAmount[] memory tokenAmounts = _getCastedSourceEVMTokenAmountsWithZeroAmounts(); - tokenAmounts[0].amount = i_tokenAmount0; - tokenAmounts[1].amount = i_tokenAmount1; - return Client.EVM2AnyMessage({ - receiver: abi.encode(OWNER), - data: "", - tokenAmounts: tokenAmounts, - feeToken: s_sourceFeeToken, - extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: GAS_LIMIT})) - }); - } - /// @dev a helper function to compose EVM2AnyRampMessage messages /// @dev it is assumed that LINK is the payment token because feeTokenAmount == feeValueJuels function _messageToEvent( @@ -105,6 +86,48 @@ contract OnRampSetup is FeeQuoterFeeSetup { ); } + function _messageToEvent( + Client.EVM2AnyMessage memory message, + uint64 sourceChainSelector, + uint64 destChainSelector, + uint64 seqNum, + uint64 nonce, + uint256 feeTokenAmount, + uint256 feeValueJuels, + address originalSender, + bytes32 metadataHash, + TokenAdminRegistry tokenAdminRegistry + ) internal view returns (Internal.EVM2AnyRampMessage memory) { + Client.EVMExtraArgsV2 memory extraArgs = + s_feeQuoter.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, + data: message.data, + receiver: message.receiver, + extraArgs: Client._argsToBytes(extraArgs), + feeToken: message.feeToken, + feeTokenAmount: feeTokenAmount, + feeValueJuels: feeValueJuels, + tokenAmounts: new Internal.EVM2AnyTokenTransfer[](message.tokenAmounts.length) + }); + + for (uint256 i = 0; i < message.tokenAmounts.length; ++i) { + messageEvent.tokenAmounts[i] = + _getSourceTokenData(message.tokenAmounts[i], tokenAdminRegistry, DEST_CHAIN_SELECTOR); + } + + messageEvent.header.messageId = Internal._hash(messageEvent, metadataHash); + return messageEvent; + } + function _generateDynamicOnRampConfig( address feeQuoter ) internal pure returns (OnRamp.DynamicConfig memory) { @@ -117,17 +140,6 @@ contract OnRampSetup is FeeQuoterFeeSetup { }); } - // Slicing is only available for calldata. So we have to build a new bytes array. - function _removeFirst4Bytes( - bytes memory data - ) internal pure returns (bytes memory) { - bytes memory result = new bytes(data.length - 4); - for (uint256 i = 4; i < data.length; ++i) { - result[i - 4] = data[i]; - } - return result; - } - function _generateDestChainConfigArgs( IRouter router ) internal pure returns (OnRamp.DestChainConfigArgs[] memory) { @@ -166,35 +178,4 @@ contract OnRampSetup is FeeQuoterFeeSetup { keccak256(abi.encode(Internal.EVM_2_ANY_MESSAGE_HASH, sourceChainSelector, DEST_CHAIN_SELECTOR, address(onRamp))) ); } - - function _enableOutboundMessageInterceptor() internal { - (, address msgSender,) = vm.readCallers(); - - bool resetPrank = false; - - if (msgSender != OWNER) { - vm.stopPrank(); - vm.startPrank(OWNER); - resetPrank = true; - } - - OnRamp.DynamicConfig memory dynamicConfig = s_onRamp.getDynamicConfig(); - dynamicConfig.messageInterceptor = address(s_outboundMessageInterceptor); - s_onRamp.setDynamicConfig(dynamicConfig); - - if (resetPrank) { - vm.stopPrank(); - vm.startPrank(msgSender); - } - } - - function _assertStaticConfigsEqual(OnRamp.StaticConfig memory a, OnRamp.StaticConfig memory b) internal pure { - assertEq(a.chainSelector, b.chainSelector); - assertEq(address(a.rmnRemote), address(b.rmnRemote)); - assertEq(a.tokenAdminRegistry, b.tokenAdminRegistry); - } - - function _assertDynamicConfigsEqual(OnRamp.DynamicConfig memory a, OnRamp.DynamicConfig memory b) internal pure { - assertEq(a.feeQuoter, b.feeQuoter); - } } diff --git a/contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool/BurnFromMintTokenPool.lockOrBurn.t.sol similarity index 76% rename from contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool.t.sol rename to contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool/BurnFromMintTokenPool.lockOrBurn.t.sol index b5967e74d1e..5074d573e2f 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool/BurnFromMintTokenPool.lockOrBurn.t.sol @@ -1,29 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {BurnFromMintTokenPool} from "../../pools/BurnFromMintTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {BurnMintSetup} from "./BurnMintSetup.t.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {BurnFromMintTokenPoolSetup} from "./BurnFromMintTokenPoolSetup.t.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract BurnFromMintTokenPoolSetup is BurnMintSetup { - BurnFromMintTokenPool internal s_pool; - - function setUp() public virtual override { - BurnMintSetup.setUp(); - - s_pool = new BurnFromMintTokenPool(s_burnMintERC677, new address[](0), address(s_mockRMN), address(s_sourceRouter)); - s_burnMintERC677.grantMintAndBurnRoles(address(s_pool)); - - _applyChainUpdates(address(s_pool)); - } -} +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; contract BurnFromMintTokenPool_lockOrBurn is BurnFromMintTokenPoolSetup { - function test_Setup_Success() public view { + function test_setup_Success() public view { assertEq(address(s_burnMintERC677), address(s_pool.getToken())); assertEq(address(s_mockRMN), s_pool.getRmnProxy()); assertEq(false, s_pool.getAllowListEnabled()); diff --git a/contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool/BurnFromMintTokenPoolSetup.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool/BurnFromMintTokenPoolSetup.t.sol new file mode 100644 index 00000000000..d743550b153 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/BurnFromMintTokenPool/BurnFromMintTokenPoolSetup.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {BurnFromMintTokenPool} from "../../../pools/BurnFromMintTokenPool.sol"; +import {BurnMintSetup} from "../BurnMintTokenPool/BurnMintSetup.t.sol"; + +contract BurnFromMintTokenPoolSetup is BurnMintSetup { + BurnFromMintTokenPool internal s_pool; + + function setUp() public virtual override { + BurnMintSetup.setUp(); + + s_pool = new BurnFromMintTokenPool(s_burnMintERC677, new address[](0), address(s_mockRMN), address(s_sourceRouter)); + s_burnMintERC677.grantMintAndBurnRoles(address(s_pool)); + + _applyChainUpdates(address(s_pool)); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintSetup.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintSetup.t.sol similarity index 83% rename from contracts/src/v0.8/ccip/test/pools/BurnMintSetup.t.sol rename to contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintSetup.t.sol index 7bf0ce57d5b..7b3d875de4c 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnMintSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintSetup.t.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {Router} from "../../Router.sol"; -import {BurnMintTokenPool} from "../../pools/BurnMintTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {RouterSetup} from "../router/RouterSetup.t.sol"; +import {BurnMintERC677} from "../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../Router.sol"; +import {BurnMintTokenPool} from "../../../pools/BurnMintTokenPool.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {RouterSetup} from "../../router/RouterSetup.t.sol"; contract BurnMintSetup is RouterSetup { BurnMintERC677 internal s_burnMintERC677; diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintTokenPool.lockOrBurn.t.sol similarity index 54% rename from contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool.t.sol rename to contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintTokenPool.lockOrBurn.t.sol index 8a6d047380c..4c520af27b6 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintTokenPool.lockOrBurn.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {BurnMintTokenPool} from "../../pools/BurnMintTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {BurnMintTokenPool} from "../../../pools/BurnMintTokenPool.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; import {BurnMintSetup} from "./BurnMintSetup.t.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; contract BurnMintTokenPoolSetup is BurnMintSetup { BurnMintTokenPool internal s_pool; @@ -98,71 +98,3 @@ contract BurnMintTokenPool_lockOrBurn is BurnMintTokenPoolSetup { ); } } - -contract BurnMintTokenPool_releaseOrMint is BurnMintTokenPoolSetup { - function test_PoolMint_Success() public { - uint256 amount = 1e19; - address receiver = makeAddr("receiver_address"); - - vm.startPrank(s_burnMintOffRamp); - - vm.expectEmit(); - emit IERC20.Transfer(address(0), receiver, amount); - - s_pool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: receiver, - amount: amount, - localToken: address(s_burnMintERC677), - remoteChainSelector: DEST_CHAIN_SELECTOR, - sourcePoolAddress: abi.encode(s_remoteBurnMintPool), - sourcePoolData: "", - offchainTokenData: "" - }) - ); - - assertEq(s_burnMintERC677.balanceOf(receiver), amount); - } - - function test_PoolMintNotHealthy_Revert() public { - // Should not mint tokens if cursed. - s_mockRMN.setGlobalCursed(true); - uint256 before = s_burnMintERC677.balanceOf(OWNER); - vm.startPrank(s_burnMintOffRamp); - - vm.expectRevert(TokenPool.CursedByRMN.selector); - s_pool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: OWNER, - amount: 1e5, - localToken: address(s_burnMintERC677), - remoteChainSelector: DEST_CHAIN_SELECTOR, - sourcePoolAddress: _generateSourceTokenData().sourcePoolAddress, - sourcePoolData: _generateSourceTokenData().extraData, - offchainTokenData: "" - }) - ); - - assertEq(s_burnMintERC677.balanceOf(OWNER), before); - } - - function test_ChainNotAllowed_Revert() public { - uint64 wrongChainSelector = 8838833; - - vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, wrongChainSelector)); - s_pool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: OWNER, - amount: 1, - localToken: address(s_burnMintERC677), - remoteChainSelector: wrongChainSelector, - sourcePoolAddress: _generateSourceTokenData().sourcePoolAddress, - sourcePoolData: _generateSourceTokenData().extraData, - offchainTokenData: "" - }) - ); - } -} diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintTokenPool.releaseOrMint.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintTokenPool.releaseOrMint.t.sol new file mode 100644 index 00000000000..e16d4c7b59d --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintTokenPool/BurnMintTokenPool.releaseOrMint.t.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Pool} from "../../../libraries/Pool.sol"; +import {BurnMintTokenPool} from "../../../pools/BurnMintTokenPool.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {BurnMintSetup} from "./BurnMintSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract BurnMintTokenPoolSetup is BurnMintSetup { + BurnMintTokenPool internal s_pool; + + function setUp() public virtual override { + BurnMintSetup.setUp(); + + s_pool = new BurnMintTokenPool(s_burnMintERC677, new address[](0), address(s_mockRMN), address(s_sourceRouter)); + s_burnMintERC677.grantMintAndBurnRoles(address(s_pool)); + + _applyChainUpdates(address(s_pool)); + } +} + +contract BurnMintTokenPool_releaseOrMint is BurnMintTokenPoolSetup { + function test_PoolMint_Success() public { + uint256 amount = 1e19; + address receiver = makeAddr("receiver_address"); + + vm.startPrank(s_burnMintOffRamp); + + vm.expectEmit(); + emit IERC20.Transfer(address(0), receiver, amount); + + s_pool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: bytes(""), + receiver: receiver, + amount: amount, + localToken: address(s_burnMintERC677), + remoteChainSelector: DEST_CHAIN_SELECTOR, + sourcePoolAddress: abi.encode(s_remoteBurnMintPool), + sourcePoolData: "", + offchainTokenData: "" + }) + ); + + assertEq(s_burnMintERC677.balanceOf(receiver), amount); + } + + function test_PoolMintNotHealthy_Revert() public { + // Should not mint tokens if cursed. + s_mockRMN.setGlobalCursed(true); + uint256 before = s_burnMintERC677.balanceOf(OWNER); + vm.startPrank(s_burnMintOffRamp); + + vm.expectRevert(TokenPool.CursedByRMN.selector); + s_pool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: bytes(""), + receiver: OWNER, + amount: 1e5, + localToken: address(s_burnMintERC677), + remoteChainSelector: DEST_CHAIN_SELECTOR, + sourcePoolAddress: _generateSourceTokenData().sourcePoolAddress, + sourcePoolData: _generateSourceTokenData().extraData, + offchainTokenData: "" + }) + ); + + assertEq(s_burnMintERC677.balanceOf(OWNER), before); + } + + function test_ChainNotAllowed_Revert() public { + uint64 wrongChainSelector = 8838833; + + vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, wrongChainSelector)); + s_pool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: bytes(""), + receiver: OWNER, + amount: 1, + localToken: address(s_burnMintERC677), + remoteChainSelector: wrongChainSelector, + sourcePoolAddress: _generateSourceTokenData().sourcePoolAddress, + sourcePoolData: _generateSourceTokenData().extraData, + offchainTokenData: "" + }) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool/BurnMintWithLockReleaseFlagTokenPool.lockOrBurn.t.sol similarity index 76% rename from contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol rename to contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool/BurnMintWithLockReleaseFlagTokenPool.lockOrBurn.t.sol index c9080a0e145..7392dc1ce83 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool/BurnMintWithLockReleaseFlagTokenPool.lockOrBurn.t.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {BurnMintWithLockReleaseFlagTokenPool} from "../../pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {BurnMintWithLockReleaseFlagTokenPool} from "../../../pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol"; -import {LOCK_RELEASE_FLAG} from "../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; -import {BurnMintSetup} from "./BurnMintSetup.t.sol"; +import {LOCK_RELEASE_FLAG} from "../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {BurnMintSetup} from "../BurnMintTokenPool/BurnMintSetup.t.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; contract BurnMintWithLockReleaseFlagTokenPoolSetup is BurnMintSetup { BurnMintWithLockReleaseFlagTokenPool internal s_pool; diff --git a/contracts/src/v0.8/ccip/test/pools/BurnWithFromMintTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.lockOrBurn.t.sol similarity index 88% rename from contracts/src/v0.8/ccip/test/pools/BurnWithFromMintTokenPool.t.sol rename to contracts/src/v0.8/ccip/test/pools/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.lockOrBurn.t.sol index 92e871708da..7595bf76c69 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnWithFromMintTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.lockOrBurn.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {BurnWithFromMintTokenPool} from "../../pools/BurnWithFromMintTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {BurnMintSetup} from "./BurnMintSetup.t.sol"; +import {Pool} from "../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {BurnWithFromMintTokenPool} from "../../../pools/BurnWithFromMintTokenPool.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {BurnMintSetup} from "../BurnMintTokenPool/BurnMintSetup.t.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; contract BurnWithFromMintTokenPoolSetup is BurnMintSetup { BurnWithFromMintTokenPool internal s_pool; diff --git a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol deleted file mode 100644 index 6f0d447f4d5..00000000000 --- a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol +++ /dev/null @@ -1,964 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {ILiquidityContainer} from "../../../liquiditymanager/interfaces/ILiquidityContainer.sol"; -import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; -import {IPoolV1} from "../../interfaces/IPool.sol"; -import {ITokenMessenger} from "../../pools/USDC/ITokenMessenger.sol"; - -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {Router} from "../../Router.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; - -import {TokenPool} from "../../pools/TokenPool.sol"; -import {HybridLockReleaseUSDCTokenPool} from "../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; -import {LOCK_RELEASE_FLAG} from "../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; -import {USDCBridgeMigrator} from "../../pools/USDC/USDCBridgeMigrator.sol"; -import {USDCTokenPool} from "../../pools/USDC/USDCTokenPool.sol"; -import {BaseTest} from "../BaseTest.t.sol"; -import {MockE2EUSDCTransmitter} from "../mocks/MockE2EUSDCTransmitter.sol"; -import {MockUSDCTokenMessenger} from "../mocks/MockUSDCTokenMessenger.sol"; - -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; - -contract USDCTokenPoolSetup is BaseTest { - IBurnMintERC20 internal s_token; - MockUSDCTokenMessenger internal s_mockUSDC; - MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; - uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; - - struct USDCMessage { - uint32 version; - uint32 sourceDomain; - uint32 destinationDomain; - uint64 nonce; - bytes32 sender; - bytes32 recipient; - bytes32 destinationCaller; - bytes messageBody; - } - - uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; - uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; - - bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); - address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); - address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); - address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); - - address internal s_routerAllowedOnRamp = address(3456); - address internal s_routerAllowedOffRamp = address(234); - Router internal s_router; - - HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; - HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; - address[] internal s_allowedList; - - function setUp() public virtual override { - BaseTest.setUp(); - BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); - s_token = usdcToken; - deal(address(s_token), OWNER, type(uint256).max); - setUpRamps(); - - s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); - s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); - - usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); - - s_usdcTokenPool = - new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); - - s_usdcTokenPoolTransferLiquidity = - new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); - - usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); - usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); - - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - remoteTokenAddress: abi.encode(address(s_token)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - chainUpdates[1] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), - remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - - s_usdcTokenPool.applyChainUpdates(chainUpdates); - - USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); - domains[0] = USDCTokenPool.DomainUpdate({ - destChainSelector: DEST_CHAIN_SELECTOR, - domainIdentifier: 9999, - allowedCaller: keccak256("allowedCaller"), - enabled: true - }); - - s_usdcTokenPool.setDomains(domains); - - vm.expectEmit(); - emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); - - s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); - s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); - } - - function setUpRamps() internal { - s_router = new Router(address(s_token), address(s_mockRMN)); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); - address[] memory offRamps = new address[](1); - offRamps[0] = s_routerAllowedOffRamp; - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); - - s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - } - - function _generateUSDCMessage( - USDCMessage memory usdcMessage - ) internal pure returns (bytes memory) { - return abi.encodePacked( - usdcMessage.version, - usdcMessage.sourceDomain, - usdcMessage.destinationDomain, - usdcMessage.nonce, - usdcMessage.sender, - usdcMessage.recipient, - usdcMessage.destinationCaller, - usdcMessage.messageBody - ); - } -} - -contract HybridUSDCTokenPoolTests is USDCTokenPoolSetup { - function test_LockOrBurn_onLockReleaseMechanism_Success() public { - bytes32 receiver = bytes32(uint256(uint160(STRANGER))); - - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - assertTrue( - s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), - "Lock/Release mech not configured for outgoing message to DEST_CHAIN_SELECTOR" - ); - - uint256 amount = 1e6; - - s_token.transfer(address(s_usdcTokenPool), amount); - - vm.startPrank(s_routerAllowedOnRamp); - - vm.expectEmit(); - emit TokenPool.Locked(s_routerAllowedOnRamp, amount); - - s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(receiver), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - - assertEq(s_token.balanceOf(address(s_usdcTokenPool)), amount, "Incorrect token amount in the tokenPool"); - } - - function test_MintOrRelease_OnLockReleaseMechanism_Success() public { - address recipient = address(1234); - - // Designate the SOURCE_CHAIN as not using native-USDC, and so the L/R mechanism must be used instead - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = SOURCE_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - assertTrue( - s_usdcTokenPool.shouldUseLockRelease(SOURCE_CHAIN_SELECTOR), - "Lock/Release mech not configured for incoming message from SOURCE_CHAIN_SELECTOR" - ); - - vm.startPrank(OWNER); - s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); - - // Add 1e12 liquidity so that there's enough to release - vm.startPrank(s_usdcTokenPool.getLiquidityProvider(SOURCE_CHAIN_SELECTOR)); - - s_token.approve(address(s_usdcTokenPool), type(uint256).max); - - uint256 liquidityAmount = 1e12; - s_usdcTokenPool.provideLiquidity(SOURCE_CHAIN_SELECTOR, liquidityAmount); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - uint256 amount = 1e6; - - vm.startPrank(s_routerAllowedOffRamp); - - vm.expectEmit(); - emit TokenPool.Released(s_routerAllowedOffRamp, recipient, amount); - - Pool.ReleaseOrMintOutV1 memory poolReturnDataV1 = s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), - offchainTokenData: "" - }) - ); - - assertEq(poolReturnDataV1.destinationAmount, amount, "destinationAmount and actual amount transferred differ"); - - // Simulate the off-ramp forwarding tokens to the recipient on destination chain - // s_token.transfer(recipient, amount); - - assertEq( - s_token.balanceOf(address(s_usdcTokenPool)), - liquidityAmount - amount, - "Incorrect remaining liquidity in TokenPool" - ); - assertEq(s_token.balanceOf(recipient), amount, "Tokens not transferred to recipient"); - } - - function test_LockOrBurn_PrimaryMechanism_Success() public { - bytes32 receiver = bytes32(uint256(uint160(STRANGER))); - uint256 amount = 1; - - vm.startPrank(OWNER); - - s_token.transfer(address(s_usdcTokenPool), amount); - - vm.startPrank(s_routerAllowedOnRamp); - - USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPool.getDomain(DEST_CHAIN_SELECTOR); - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - - vm.expectEmit(); - emit ITokenMessenger.DepositForBurn( - s_mockUSDC.s_nonce(), - address(s_token), - amount, - address(s_usdcTokenPool), - receiver, - expectedDomain.domainIdentifier, - s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), - expectedDomain.allowedCaller - ); - - vm.expectEmit(); - emit TokenPool.Burned(s_routerAllowedOnRamp, amount); - - Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(receiver), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - - uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); - assertEq(s_mockUSDC.s_nonce() - 1, nonce); - } - - // https://etherscan.io/tx/0xac9f501fe0b76df1f07a22e1db30929fd12524bc7068d74012dff948632f0883 - function test_MintOrRelease_incomingMessageWithPrimaryMechanism() public { - bytes memory encodedUsdcMessage = - hex"000000000000000300000000000000000000127a00000000000000000000000019330d10d9cc8751218eaf51e8885d058642e08a000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e58310000000000000000000000004af08f56978be7dce2d1be3c65c005b41e79401c000000000000000000000000000000000000000000000000000000002057ff7a0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000000000000000000000000000000000000000000000000000000000008274119237535fd659626b090f87e365ff89ebc7096bb32e8b0e85f155626b73ae7c4bb2485c184b7cc3cf7909045487890b104efb62ae74a73e32901bdcec91df1bb9ee08ccb014fcbcfe77b74d1263fd4e0b0e8de05d6c9a5913554364abfd5ea768b222f50c715908183905d74044bb2b97527c7e70ae7983c443a603557cac3b1c000000000000000000000000000000000000000000000000000000000000"; - bytes memory attestation = bytes("attestation bytes"); - - uint32 nonce = 4730; - uint32 sourceDomain = 3; - uint256 amount = 100; - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: nonce, sourceDomain: sourceDomain})), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - // The mocked receiver does not release the token to the pool, so we manually do it here - deal(address(s_token), address(s_usdcTokenPool), amount); - - bytes memory offchainTokenData = - abi.encode(USDCTokenPool.MessageAndAttestation({message: encodedUsdcMessage, attestation: attestation})); - - vm.expectCall( - address(s_mockUSDCTransmitter), - abi.encodeWithSelector(MockE2EUSDCTransmitter.receiveMessage.selector, encodedUsdcMessage, attestation) - ); - - vm.startPrank(s_routerAllowedOffRamp); - s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: OWNER, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData - }) - ); - } - - function test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() public { - // Test Enabling the LR mechanism and sending an outgoing message - test_LockOrBurn_PrimaryMechanism_Success(); - - // Disable the LR mechanism so that primary CCTP is used and then attempt to send a message - uint64[] memory destChainRemoves = new uint64[](1); - destChainRemoves[0] = DEST_CHAIN_SELECTOR; - - vm.startPrank(OWNER); - - vm.expectEmit(); - emit HybridLockReleaseUSDCTokenPool.LockReleaseDisabled(DEST_CHAIN_SELECTOR); - - s_usdcTokenPool.updateChainSelectorMechanisms(destChainRemoves, new uint64[](0)); - - // Send an outgoing message - test_LockOrBurn_PrimaryMechanism_Success(); - } - - function test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() public { - test_MintOrRelease_OnLockReleaseMechanism_Success(); - - // Disable the LR mechanism so that primary CCTP is used and then attempt to send a message - uint64[] memory destChainRemoves = new uint64[](1); - destChainRemoves[0] = SOURCE_CHAIN_SELECTOR; - - vm.startPrank(OWNER); - - vm.expectEmit(); - emit HybridLockReleaseUSDCTokenPool.LockReleaseDisabled(SOURCE_CHAIN_SELECTOR); - - s_usdcTokenPool.updateChainSelectorMechanisms(destChainRemoves, new uint64[](0)); - - vm.expectEmit(); - emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(OWNER, OWNER, SOURCE_CHAIN_SELECTOR); - - s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); - - // Test incoming on the primary mechanism after disable LR, simulating Circle's new support for CCTP on - // DEST_CHAIN_SELECTOR - test_MintOrRelease_incomingMessageWithPrimaryMechanism(); - } - - function test_LockOrBurn_WhileMigrationPause_Revert() public { - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - // Create a fake migration proposal - s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); - - assertEq(s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), DEST_CHAIN_SELECTOR); - - bytes32 receiver = bytes32(uint256(uint160(STRANGER))); - - assertTrue( - s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), - "Lock Release mech not configured for outgoing message to DEST_CHAIN_SELECTOR" - ); - - uint256 amount = 1e6; - - s_token.transfer(address(s_usdcTokenPool), amount); - - vm.startPrank(s_routerAllowedOnRamp); - - // Expect the lockOrBurn to fail because a pending CCTP-Migration has paused outgoing messages on CCIP - vm.expectRevert( - abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, DEST_CHAIN_SELECTOR) - ); - - s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(receiver), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - } - - function test_ReleaseOrMint_WhileMigrationPause_Revert() public { - address recipient = address(1234); - - // Designate the SOURCE_CHAIN as not using native-USDC, and so the L/R mechanism must be used instead - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = SOURCE_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - assertTrue( - s_usdcTokenPool.shouldUseLockRelease(SOURCE_CHAIN_SELECTOR), - "Lock/Release mech not configured for incoming message from SOURCE_CHAIN_SELECTOR" - ); - - vm.startPrank(OWNER); - - vm.expectEmit(); - emit USDCBridgeMigrator.CCTPMigrationProposed(SOURCE_CHAIN_SELECTOR); - - // Propose the migration to CCTP - s_usdcTokenPool.proposeCCTPMigration(SOURCE_CHAIN_SELECTOR); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - bytes memory sourcePoolDataLockRelease = abi.encode(LOCK_RELEASE_FLAG); - - uint256 amount = 1e6; - - vm.startPrank(s_routerAllowedOffRamp); - - // Expect revert because the lane is paused and no incoming messages should be allowed - vm.expectRevert( - abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, SOURCE_CHAIN_SELECTOR) - ); - - s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourcePoolDataLockRelease, - offchainTokenData: "" - }) - ); - } - - function test_transferLiquidity_Success() public { - // Set as the OWNER so we can provide liquidity - vm.startPrank(OWNER); - - s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); - s_token.approve(address(s_usdcTokenPool), type(uint256).max); - - uint256 liquidityAmount = 1e9; - - // Provide some liquidity to the pool - s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, liquidityAmount); - - // Set the new token pool as the rebalancer - s_usdcTokenPool.transferOwnership(address(s_usdcTokenPoolTransferLiquidity)); - - vm.expectEmit(); - emit ILiquidityContainer.LiquidityRemoved(address(s_usdcTokenPoolTransferLiquidity), liquidityAmount); - - vm.expectEmit(); - emit HybridLockReleaseUSDCTokenPool.LiquidityTransferred( - address(s_usdcTokenPool), DEST_CHAIN_SELECTOR, liquidityAmount - ); - - s_usdcTokenPoolTransferLiquidity.transferLiquidity(address(s_usdcTokenPool), DEST_CHAIN_SELECTOR); - - assertEq( - s_usdcTokenPool.owner(), - address(s_usdcTokenPoolTransferLiquidity), - "Ownership of the old pool should be transferred to the new pool" - ); - - assertEq( - s_usdcTokenPoolTransferLiquidity.getLockedTokensForChain(DEST_CHAIN_SELECTOR), - liquidityAmount, - "Tokens locked for dest chain doesn't match expected amount in storage" - ); - - assertEq( - s_usdcTokenPool.getLockedTokensForChain(DEST_CHAIN_SELECTOR), - 0, - "Tokens locked for dest chain in old token pool doesn't match expected amount in storage" - ); - - assertEq( - s_token.balanceOf(address(s_usdcTokenPoolTransferLiquidity)), - liquidityAmount, - "Liquidity amount of tokens should be new in new pool, but aren't" - ); - - assertEq( - s_token.balanceOf(address(s_usdcTokenPool)), - 0, - "Liquidity amount of tokens should be zero in old pool, but aren't" - ); - } - - function test_cannotTransferLiquidityDuringPendingMigration_Revert() public { - // Set as the OWNER so we can provide liquidity - vm.startPrank(OWNER); - - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); - s_token.approve(address(s_usdcTokenPool), type(uint256).max); - - uint256 liquidityAmount = 1e9; - - // Provide some liquidity to the pool - s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, liquidityAmount); - - // Set the new token pool as the rebalancer - s_usdcTokenPool.transferOwnership(address(s_usdcTokenPoolTransferLiquidity)); - - s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); - - vm.expectRevert( - abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, DEST_CHAIN_SELECTOR) - ); - - s_usdcTokenPoolTransferLiquidity.transferLiquidity(address(s_usdcTokenPool), DEST_CHAIN_SELECTOR); - } -} - -contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { - function test_lockOrBurn_then_BurnInCCTPMigration_Success() public { - bytes32 receiver = bytes32(uint256(uint160(STRANGER))); - address CIRCLE = makeAddr("CIRCLE CCTP Migrator"); - - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - assertTrue( - s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), - "Lock/Release mech not configured for outgoing message to DEST_CHAIN_SELECTOR" - ); - - uint256 amount = 1e6; - - s_token.transfer(address(s_usdcTokenPool), amount); - - vm.startPrank(s_routerAllowedOnRamp); - - vm.expectEmit(); - emit TokenPool.Locked(s_routerAllowedOnRamp, amount); - - s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(receiver), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - - // Ensure that the tokens are properly locked - assertEq(s_token.balanceOf(address(s_usdcTokenPool)), amount, "Incorrect token amount in the tokenPool"); - - assertEq( - s_usdcTokenPool.getLockedTokensForChain(DEST_CHAIN_SELECTOR), - amount, - "Internal locked token accounting is incorrect" - ); - - vm.startPrank(OWNER); - - vm.expectEmit(); - emit USDCBridgeMigrator.CircleMigratorAddressSet(CIRCLE); - - s_usdcTokenPool.setCircleMigratorAddress(CIRCLE); - - vm.expectEmit(); - emit USDCBridgeMigrator.CCTPMigrationProposed(DEST_CHAIN_SELECTOR); - - // Propose the migration to CCTP - s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); - - assertEq( - s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), - DEST_CHAIN_SELECTOR, - "Current proposed chain migration does not match expected for DEST_CHAIN_SELECTOR" - ); - - // Impersonate the set circle address and execute the proposal - vm.startPrank(CIRCLE); - - vm.expectEmit(); - emit USDCBridgeMigrator.CCTPMigrationExecuted(DEST_CHAIN_SELECTOR, amount); - - // Ensure the call to the burn function is properly - vm.expectCall(address(s_token), abi.encodeWithSelector(bytes4(keccak256("burn(uint256)")), amount)); - - s_usdcTokenPool.burnLockedUSDC(); - - // Assert that the tokens were actually burned - assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 0, "Tokens were not burned out of the tokenPool"); - - // Ensure the proposal slot was cleared and there's no tokens locked for the destination chain anymore - assertEq(s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), 0, "Proposal Slot should be empty"); - assertEq( - s_usdcTokenPool.getLockedTokensForChain(DEST_CHAIN_SELECTOR), - 0, - "No tokens should be locked for DEST_CHAIN_SELECTOR after CCTP-approved burn" - ); - - assertFalse( - s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), "Lock/Release mech should be disabled after a burn" - ); - - test_LockOrBurn_PrimaryMechanism_Success(); - } - - function test_cancelExistingCCTPMigrationProposal() public { - vm.startPrank(OWNER); - - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - vm.expectEmit(); - emit USDCBridgeMigrator.CCTPMigrationProposed(DEST_CHAIN_SELECTOR); - - s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); - - assertEq( - s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), - DEST_CHAIN_SELECTOR, - "migration proposal should exist, but doesn't" - ); - - vm.expectEmit(); - emit USDCBridgeMigrator.CCTPMigrationCancelled(DEST_CHAIN_SELECTOR); - - s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); - - assertEq( - s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), - 0, - "migration proposal exists, but shouldn't after being cancelled" - ); - - vm.expectRevert(USDCBridgeMigrator.NoMigrationProposalPending.selector); - s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); - } - - function test_burnLockedUSDC_invalidPermissions_Revert() public { - address CIRCLE = makeAddr("CIRCLE"); - - vm.startPrank(OWNER); - - // Set the circle migrator address for later, but don't start pranking as it yet - s_usdcTokenPool.setCircleMigratorAddress(CIRCLE); - - vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.onlyCircle.selector)); - - // Should fail because only Circle can call this function - s_usdcTokenPool.burnLockedUSDC(); - - vm.startPrank(CIRCLE); - - vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.NoMigrationProposalPending.selector)); - s_usdcTokenPool.burnLockedUSDC(); - } - - function test_cannotModifyLiquidityWithoutPermissions_Revert() public { - address randomAddr = makeAddr("RANDOM"); - - vm.startPrank(randomAddr); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, randomAddr)); - - // Revert because there's insufficient permissions for the DEST_CHAIN_SELECTOR to provide liquidity - s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); - } - - function test_cannotCancelANonExistentMigrationProposal() public { - vm.expectRevert(USDCBridgeMigrator.NoMigrationProposalPending.selector); - - // Proposal to migrate doesn't exist, and so the chain selector is zero, and therefore should revert - s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); - } - - function test_unstickManualTxAfterMigration_destChain_Success() public { - address recipient = address(1234); - // Test the edge case where a tx is stuck in the manual tx queue and the destination chain is the one that - // should process is after a migration. I.E the message will have the Lock-Release flag set in the OffChainData, - // which should tell it to use the lock-release mechanism with the tokens provided. - - // We want the released amount to be 1e6, so to simulate the workflow, we sent those tokens to the contract as - // liquidity - uint256 amount = 1e6; - // Add 1e12 liquidity so that there's enough to release - vm.startPrank(s_usdcTokenPool.getLiquidityProvider(SOURCE_CHAIN_SELECTOR)); - - s_token.approve(address(s_usdcTokenPool), type(uint256).max); - s_usdcTokenPool.provideLiquidity(SOURCE_CHAIN_SELECTOR, amount); - - // By Default, the source chain will be indicated as use-CCTP so we need to change that. We create a message - // that will use the Lock-Release flag in the offchain data to indicate that the tokens should be released - // instead of minted since there's no attestation for us to use. - - vm.startPrank(s_routerAllowedOffRamp); - - vm.expectEmit(); - emit TokenPool.Released(s_routerAllowedOffRamp, recipient, amount); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - Pool.ReleaseOrMintOutV1 memory poolReturnDataV1 = s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), - offchainTokenData: "" - }) - ); - - // By this point, the tx should have executed, with the Lock-Release taking over, and being forwaded to the - // recipient - - assertEq(poolReturnDataV1.destinationAmount, amount, "destinationAmount and actual amount transferred differ"); - assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 0, "Tokens should be transferred out of the pool"); - assertEq(s_token.balanceOf(recipient), amount, "Tokens should be transferred to the recipient"); - - // We also want to check that the system uses CCTP Burn/Mint for all other messages that don't have that flag - // which after a migration will mean all new messages. - - // The message should fail without an error because it failed to decode a non-existent attestation which would - // revert without an error - vm.expectRevert(); - - s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: "", - offchainTokenData: "" - }) - ); - } - - function test_unstickManualTxAfterMigration_homeChain_Success() public { - address CIRCLE = makeAddr("CIRCLE"); - address recipient = address(1234); - - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = SOURCE_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - // Test the edge case where a tx is stuck in the manual tx queue and the source chain (mainnet) needs unsticking - // In this test we want 1e6 worth of tokens to be stuck, so first we provide liquidity to the pool >1e6 - - uint256 amount = 1e6; - // Add 1e12 liquidity so that there's enough to release - vm.startPrank(s_usdcTokenPool.getLiquidityProvider(SOURCE_CHAIN_SELECTOR)); - - s_token.approve(address(s_usdcTokenPool), type(uint256).max); - - // I picked 3x the amount to be stuck so that we can have enough to release with a buffer - s_usdcTokenPool.provideLiquidity(SOURCE_CHAIN_SELECTOR, amount * 3); - - // At this point in the process, the router will lock new messages, so we want to simulate excluding tokens - // stuck coming back from the destination, to the home chain. This way they can be released and not minted - // since there's no corresponding attestation to use for minting. - vm.startPrank(OWNER); - - s_usdcTokenPool.proposeCCTPMigration(SOURCE_CHAIN_SELECTOR); - - // Exclude the tokens from being burned and check for the event - vm.expectEmit(); - emit USDCBridgeMigrator.TokensExcludedFromBurn(SOURCE_CHAIN_SELECTOR, amount, (amount * 3) - amount); - - s_usdcTokenPool.excludeTokensFromBurn(SOURCE_CHAIN_SELECTOR, amount); - - assertEq( - s_usdcTokenPool.getLockedTokensForChain(SOURCE_CHAIN_SELECTOR), - (amount * 3), - "Tokens locked minus ones excluded from the burn should be 2e6" - ); - - assertEq( - s_usdcTokenPool.getExcludedTokensByChain(SOURCE_CHAIN_SELECTOR), - 1e6, - "1e6 tokens should be excluded from the burn" - ); - - s_usdcTokenPool.setCircleMigratorAddress(CIRCLE); - - vm.startPrank(CIRCLE); - - s_usdcTokenPool.burnLockedUSDC(); - - assertEq( - s_usdcTokenPool.getLockedTokensForChain(SOURCE_CHAIN_SELECTOR), 0, "All tokens should be burned out of the pool" - ); - - assertEq( - s_usdcTokenPool.getExcludedTokensByChain(SOURCE_CHAIN_SELECTOR), - 1e6, - "There should still be 1e6 tokens excluded from the burn" - ); - - assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 1e6, "All tokens minus the excluded should be in the pool"); - - // Now that the burn is successful, we can release the tokens that were excluded from the burn - vm.startPrank(s_routerAllowedOffRamp); - - vm.expectEmit(); - emit TokenPool.Released(s_routerAllowedOffRamp, recipient, amount); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - Pool.ReleaseOrMintOutV1 memory poolReturnDataV1 = s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), - offchainTokenData: "" - }) - ); - - assertEq(poolReturnDataV1.destinationAmount, amount, "destinationAmount and actual amount transferred differ"); - assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 0, "Tokens should be transferred out of the pool"); - assertEq(s_token.balanceOf(recipient), amount, "Tokens should be transferred to the recipient"); - assertEq( - s_usdcTokenPool.getExcludedTokensByChain(SOURCE_CHAIN_SELECTOR), - 0, - "All tokens should be released from the exclusion list" - ); - - // We also want to check that the system uses CCTP Burn/Mint for all other messages that don't have that flag - test_MintOrRelease_incomingMessageWithPrimaryMechanism(); - } - - function test_ProposeMigration_ChainNotUsingLockRelease_Revert() public { - vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.InvalidChainSelector.selector)); - - vm.startPrank(OWNER); - - s_usdcTokenPool.proposeCCTPMigration(0x98765); - } - - function test_excludeTokensWhenNoMigrationProposalPending_Revert() public { - vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.NoMigrationProposalPending.selector)); - - vm.startPrank(OWNER); - - s_usdcTokenPool.excludeTokensFromBurn(SOURCE_CHAIN_SELECTOR, 1e6); - } - - function test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() public { - vm.startPrank(OWNER); - - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - - s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); - - vm.expectRevert( - abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, DEST_CHAIN_SELECTOR) - ); - s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); - } - - function test_cannotRevertChainMechanism_afterMigration_Revert() public { - test_lockOrBurn_then_BurnInCCTPMigration_Success(); - - vm.startPrank(OWNER); - - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - vm.expectRevert( - abi.encodeWithSelector( - HybridLockReleaseUSDCTokenPool.TokenLockingNotAllowedAfterMigration.selector, DEST_CHAIN_SELECTOR - ) - ); - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - } - - function test_cnanotProvideLiquidity_AfterMigration_Revert() public { - test_lockOrBurn_then_BurnInCCTPMigration_Success(); - - vm.startPrank(OWNER); - - vm.expectRevert( - abi.encodeWithSelector( - HybridLockReleaseUSDCTokenPool.TokenLockingNotAllowedAfterMigration.selector, DEST_CHAIN_SELECTOR - ) - ); - - s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); - } -} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool.t.sol deleted file mode 100644 index f17ef80b4d5..00000000000 --- a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool.t.sol +++ /dev/null @@ -1,439 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IPoolV1} from "../../interfaces/IPool.sol"; - -import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {Router} from "../../Router.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {LockReleaseTokenPool} from "../../pools/LockReleaseTokenPool.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol"; -import {RouterSetup} from "../router/RouterSetup.t.sol"; - -contract LockReleaseTokenPoolSetup is RouterSetup { - IERC20 internal s_token; - LockReleaseTokenPool internal s_lockReleaseTokenPool; - LockReleaseTokenPool internal s_lockReleaseTokenPoolWithAllowList; - address[] internal s_allowedList; - - address internal s_allowedOnRamp = address(123); - address internal s_allowedOffRamp = address(234); - - address internal s_destPoolAddress = address(2736782345); - address internal s_sourcePoolAddress = address(53852352095); - - function setUp() public virtual override { - RouterSetup.setUp(); - s_token = new BurnMintERC677("LINK", "LNK", 18, 0); - deal(address(s_token), OWNER, type(uint256).max); - s_lockReleaseTokenPool = - new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), true, address(s_sourceRouter)); - - s_allowedList.push(USER_1); - s_allowedList.push(DUMMY_CONTRACT_ADDRESS); - s_lockReleaseTokenPoolWithAllowList = - new LockReleaseTokenPool(s_token, s_allowedList, address(s_mockRMN), true, address(s_sourceRouter)); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_destPoolAddress), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - - s_lockReleaseTokenPool.applyChainUpdates(chainUpdate); - s_lockReleaseTokenPoolWithAllowList.applyChainUpdates(chainUpdate); - s_lockReleaseTokenPool.setRebalancer(OWNER); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_allowedOnRamp}); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: s_allowedOffRamp}); - s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - } -} - -contract LockReleaseTokenPool_setRebalancer is LockReleaseTokenPoolSetup { - function test_SetRebalancer_Success() public { - assertEq(address(s_lockReleaseTokenPool.getRebalancer()), OWNER); - s_lockReleaseTokenPool.setRebalancer(STRANGER); - assertEq(address(s_lockReleaseTokenPool.getRebalancer()), STRANGER); - } - - function test_SetRebalancer_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - s_lockReleaseTokenPool.setRebalancer(STRANGER); - } -} - -contract LockReleaseTokenPool_lockOrBurn is LockReleaseTokenPoolSetup { - function test_Fuzz_LockOrBurnNoAllowList_Success( - uint256 amount - ) public { - amount = bound(amount, 1, _getOutboundRateLimiterConfig().capacity); - vm.startPrank(s_allowedOnRamp); - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - vm.expectEmit(); - emit TokenPool.Locked(s_allowedOnRamp, amount); - - s_lockReleaseTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: STRANGER, - receiver: bytes(""), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - } - - function test_LockOrBurnWithAllowList_Success() public { - uint256 amount = 100; - vm.startPrank(s_allowedOnRamp); - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - vm.expectEmit(); - emit TokenPool.Locked(s_allowedOnRamp, amount); - - s_lockReleaseTokenPoolWithAllowList.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: s_allowedList[0], - receiver: bytes(""), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - - vm.expectEmit(); - emit TokenPool.Locked(s_allowedOnRamp, amount); - - s_lockReleaseTokenPoolWithAllowList.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: s_allowedList[1], - receiver: bytes(""), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - } - - function test_LockOrBurnWithAllowList_Revert() public { - vm.startPrank(s_allowedOnRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.SenderNotAllowed.selector, STRANGER)); - - s_lockReleaseTokenPoolWithAllowList.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: STRANGER, - receiver: bytes(""), - amount: 100, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - } - - function test_PoolBurnRevertNotHealthy_Revert() public { - // Should not burn tokens if cursed. - s_mockRMN.setGlobalCursed(true); - uint256 before = s_token.balanceOf(address(s_lockReleaseTokenPoolWithAllowList)); - - vm.startPrank(s_allowedOnRamp); - vm.expectRevert(TokenPool.CursedByRMN.selector); - - s_lockReleaseTokenPoolWithAllowList.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: s_allowedList[0], - receiver: bytes(""), - amount: 1e5, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - - assertEq(s_token.balanceOf(address(s_lockReleaseTokenPoolWithAllowList)), before); - } -} - -contract LockReleaseTokenPool_releaseOrMint is LockReleaseTokenPoolSetup { - function setUp() public virtual override { - LockReleaseTokenPoolSetup.setUp(); - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(s_sourcePoolAddress), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - - s_lockReleaseTokenPool.applyChainUpdates(chainUpdate); - s_lockReleaseTokenPoolWithAllowList.applyChainUpdates(chainUpdate); - } - - function test_ReleaseOrMint_Success() public { - vm.startPrank(s_allowedOffRamp); - - uint256 amount = 100; - deal(address(s_token), address(s_lockReleaseTokenPool), amount); - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - vm.expectEmit(); - emit TokenPool.Released(s_allowedOffRamp, OWNER, amount); - - s_lockReleaseTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: OWNER, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: abi.encode(s_sourcePoolAddress), - sourcePoolData: "", - offchainTokenData: "" - }) - ); - } - - function test_Fuzz_ReleaseOrMint_Success(address recipient, uint256 amount) public { - // Since the owner already has tokens this would break the checks - vm.assume(recipient != OWNER); - vm.assume(recipient != address(0)); - vm.assume(recipient != address(s_token)); - - // Makes sure the pool always has enough funds - deal(address(s_token), address(s_lockReleaseTokenPool), amount); - vm.startPrank(s_allowedOffRamp); - - uint256 capacity = _getInboundRateLimiterConfig().capacity; - // Determine if we hit the rate limit or the txs should succeed. - if (amount > capacity) { - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_token)) - ); - } else { - // Only rate limit if the amount is >0 - if (amount > 0) { - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - } - - vm.expectEmit(); - emit TokenPool.Released(s_allowedOffRamp, recipient, amount); - } - - s_lockReleaseTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: abi.encode(s_sourcePoolAddress), - sourcePoolData: "", - offchainTokenData: "" - }) - ); - } - - function test_ChainNotAllowed_Revert() public { - address notAllowedRemotePoolAddress = address(1); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(notAllowedRemotePoolAddress), - remoteTokenAddress: abi.encode(address(2)), - allowed: false, - outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), - inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) - }); - - s_lockReleaseTokenPool.applyChainUpdates(chainUpdate); - - vm.startPrank(s_allowedOffRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, SOURCE_CHAIN_SELECTOR)); - s_lockReleaseTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: OWNER, - amount: 1e5, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: abi.encode(s_sourcePoolAddress), - sourcePoolData: "", - offchainTokenData: "" - }) - ); - } - - function test_PoolMintNotHealthy_Revert() public { - // Should not mint tokens if cursed. - s_mockRMN.setGlobalCursed(true); - uint256 before = s_token.balanceOf(OWNER); - vm.startPrank(s_allowedOffRamp); - vm.expectRevert(TokenPool.CursedByRMN.selector); - s_lockReleaseTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: bytes(""), - receiver: OWNER, - amount: 1e5, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: _generateSourceTokenData().sourcePoolAddress, - sourcePoolData: _generateSourceTokenData().extraData, - offchainTokenData: "" - }) - ); - - assertEq(s_token.balanceOf(OWNER), before); - } -} - -contract LockReleaseTokenPool_canAcceptLiquidity is LockReleaseTokenPoolSetup { - function test_CanAcceptLiquidity_Success() public { - assertEq(true, s_lockReleaseTokenPool.canAcceptLiquidity()); - - s_lockReleaseTokenPool = - new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), false, address(s_sourceRouter)); - assertEq(false, s_lockReleaseTokenPool.canAcceptLiquidity()); - } -} - -contract LockReleaseTokenPool_provideLiquidity is LockReleaseTokenPoolSetup { - function test_Fuzz_ProvideLiquidity_Success( - uint256 amount - ) public { - uint256 balancePre = s_token.balanceOf(OWNER); - s_token.approve(address(s_lockReleaseTokenPool), amount); - - s_lockReleaseTokenPool.provideLiquidity(amount); - - assertEq(s_token.balanceOf(OWNER), balancePre - amount); - assertEq(s_token.balanceOf(address(s_lockReleaseTokenPool)), amount); - } - - // Reverts - - function test_Unauthorized_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); - - s_lockReleaseTokenPool.provideLiquidity(1); - } - - function test_Fuzz_ExceedsAllowance( - uint256 amount - ) public { - vm.assume(amount > 0); - vm.expectRevert("ERC20: insufficient allowance"); - s_lockReleaseTokenPool.provideLiquidity(amount); - } - - function test_LiquidityNotAccepted_Revert() public { - s_lockReleaseTokenPool = - new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), false, address(s_sourceRouter)); - - vm.expectRevert(LockReleaseTokenPool.LiquidityNotAccepted.selector); - s_lockReleaseTokenPool.provideLiquidity(1); - } -} - -contract LockReleaseTokenPool_withdrawalLiquidity is LockReleaseTokenPoolSetup { - function test_Fuzz_WithdrawalLiquidity_Success( - uint256 amount - ) public { - uint256 balancePre = s_token.balanceOf(OWNER); - s_token.approve(address(s_lockReleaseTokenPool), amount); - s_lockReleaseTokenPool.provideLiquidity(amount); - - s_lockReleaseTokenPool.withdrawLiquidity(amount); - - assertEq(s_token.balanceOf(OWNER), balancePre); - } - - // Reverts - - function test_Unauthorized_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); - - s_lockReleaseTokenPool.withdrawLiquidity(1); - } - - function test_InsufficientLiquidity_Revert() public { - uint256 maxUint256 = 2 ** 256 - 1; - s_token.approve(address(s_lockReleaseTokenPool), maxUint256); - s_lockReleaseTokenPool.provideLiquidity(maxUint256); - - vm.startPrank(address(s_lockReleaseTokenPool)); - s_token.transfer(OWNER, maxUint256); - vm.startPrank(OWNER); - - vm.expectRevert(LockReleaseTokenPool.InsufficientLiquidity.selector); - s_lockReleaseTokenPool.withdrawLiquidity(1); - } -} - -contract LockReleaseTokenPool_transferLiquidity is LockReleaseTokenPoolSetup { - LockReleaseTokenPool internal s_oldLockReleaseTokenPool; - uint256 internal s_amount = 100000; - - function setUp() public virtual override { - super.setUp(); - - s_oldLockReleaseTokenPool = - new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), true, address(s_sourceRouter)); - - deal(address(s_token), address(s_oldLockReleaseTokenPool), s_amount); - } - - function test_transferLiquidity_Success() public { - uint256 balancePre = s_token.balanceOf(address(s_lockReleaseTokenPool)); - - s_oldLockReleaseTokenPool.setRebalancer(address(s_lockReleaseTokenPool)); - - vm.expectEmit(); - emit LockReleaseTokenPool.LiquidityTransferred(address(s_oldLockReleaseTokenPool), s_amount); - - s_lockReleaseTokenPool.transferLiquidity(address(s_oldLockReleaseTokenPool), s_amount); - - assertEq(s_token.balanceOf(address(s_lockReleaseTokenPool)), balancePre + s_amount); - } - - function test_transferLiquidity_transferTooMuch_Revert() public { - uint256 balancePre = s_token.balanceOf(address(s_lockReleaseTokenPool)); - - s_oldLockReleaseTokenPool.setRebalancer(address(s_lockReleaseTokenPool)); - - vm.expectRevert(LockReleaseTokenPool.InsufficientLiquidity.selector); - s_lockReleaseTokenPool.transferLiquidity(address(s_oldLockReleaseTokenPool), s_amount + 1); - - assertEq(s_token.balanceOf(address(s_lockReleaseTokenPool)), balancePre); - } -} - -contract LockReleaseTokenPool_supportsInterface is LockReleaseTokenPoolSetup { - function test_SupportsInterface_Success() public view { - assertTrue(s_lockReleaseTokenPool.supportsInterface(type(IPoolV1).interfaceId)); - assertTrue(s_lockReleaseTokenPool.supportsInterface(type(IERC165).interfaceId)); - } -} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.canAcceptLiquidity.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.canAcceptLiquidity.t.sol new file mode 100644 index 00000000000..9a124572f96 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.canAcceptLiquidity.t.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +contract LockReleaseTokenPool_canAcceptLiquidity is LockReleaseTokenPoolSetup { + function test_CanAcceptLiquidity_Success() public { + assertEq(true, s_lockReleaseTokenPool.canAcceptLiquidity()); + + s_lockReleaseTokenPool = + new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), false, address(s_sourceRouter)); + assertEq(false, s_lockReleaseTokenPool.canAcceptLiquidity()); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.lockOrBurn.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.lockOrBurn.t.sol new file mode 100644 index 00000000000..667386ae7e0 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.lockOrBurn.t.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Pool} from "../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +contract LockReleaseTokenPool_lockOrBurn is LockReleaseTokenPoolSetup { + function test_Fuzz_LockOrBurnNoAllowList_Success( + uint256 amount + ) public { + amount = bound(amount, 1, _getOutboundRateLimiterConfig().capacity); + vm.startPrank(s_allowedOnRamp); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + vm.expectEmit(); + emit TokenPool.Locked(s_allowedOnRamp, amount); + + s_lockReleaseTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: STRANGER, + receiver: bytes(""), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + } + + function test_LockOrBurnWithAllowList_Success() public { + uint256 amount = 100; + vm.startPrank(s_allowedOnRamp); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + vm.expectEmit(); + emit TokenPool.Locked(s_allowedOnRamp, amount); + + s_lockReleaseTokenPoolWithAllowList.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: s_allowedList[0], + receiver: bytes(""), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + + vm.expectEmit(); + emit TokenPool.Locked(s_allowedOnRamp, amount); + + s_lockReleaseTokenPoolWithAllowList.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: s_allowedList[1], + receiver: bytes(""), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + } + + function test_LockOrBurnWithAllowList_Revert() public { + vm.startPrank(s_allowedOnRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.SenderNotAllowed.selector, STRANGER)); + + s_lockReleaseTokenPoolWithAllowList.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: STRANGER, + receiver: bytes(""), + amount: 100, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + } + + function test_PoolBurnRevertNotHealthy_Revert() public { + // Should not burn tokens if cursed. + s_mockRMN.setGlobalCursed(true); + uint256 before = s_token.balanceOf(address(s_lockReleaseTokenPoolWithAllowList)); + + vm.startPrank(s_allowedOnRamp); + vm.expectRevert(TokenPool.CursedByRMN.selector); + + s_lockReleaseTokenPoolWithAllowList.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: s_allowedList[0], + receiver: bytes(""), + amount: 1e5, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + + assertEq(s_token.balanceOf(address(s_lockReleaseTokenPoolWithAllowList)), before); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.provideLiquidity.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.provideLiquidity.t.sol new file mode 100644 index 00000000000..664d9526063 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.provideLiquidity.t.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +contract LockReleaseTokenPool_provideLiquidity is LockReleaseTokenPoolSetup { + function test_Fuzz_ProvideLiquidity_Success( + uint256 amount + ) public { + uint256 balancePre = s_token.balanceOf(OWNER); + s_token.approve(address(s_lockReleaseTokenPool), amount); + + s_lockReleaseTokenPool.provideLiquidity(amount); + + assertEq(s_token.balanceOf(OWNER), balancePre - amount); + assertEq(s_token.balanceOf(address(s_lockReleaseTokenPool)), amount); + } + + // Reverts + + function test_Unauthorized_Revert() public { + vm.startPrank(STRANGER); + vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); + + s_lockReleaseTokenPool.provideLiquidity(1); + } + + function test_Fuzz_ExceedsAllowance( + uint256 amount + ) public { + vm.assume(amount > 0); + vm.expectRevert("ERC20: insufficient allowance"); + s_lockReleaseTokenPool.provideLiquidity(amount); + } + + function test_LiquidityNotAccepted_Revert() public { + s_lockReleaseTokenPool = + new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), false, address(s_sourceRouter)); + + vm.expectRevert(LockReleaseTokenPool.LiquidityNotAccepted.selector); + s_lockReleaseTokenPool.provideLiquidity(1); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.releaseOrMint.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.releaseOrMint.t.sol new file mode 100644 index 00000000000..06ccfc38065 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.releaseOrMint.t.sol @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Pool} from "../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +contract LockReleaseTokenPool_releaseOrMint is LockReleaseTokenPoolSetup { + function setUp() public virtual override { + LockReleaseTokenPoolSetup.setUp(); + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(s_sourcePoolAddress), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_lockReleaseTokenPool.applyChainUpdates(chainUpdate); + s_lockReleaseTokenPoolWithAllowList.applyChainUpdates(chainUpdate); + } + + function test_ReleaseOrMint_Success() public { + vm.startPrank(s_allowedOffRamp); + + uint256 amount = 100; + deal(address(s_token), address(s_lockReleaseTokenPool), amount); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + vm.expectEmit(); + emit TokenPool.Released(s_allowedOffRamp, OWNER, amount); + + s_lockReleaseTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: bytes(""), + receiver: OWNER, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: abi.encode(s_sourcePoolAddress), + sourcePoolData: "", + offchainTokenData: "" + }) + ); + } + + function test_Fuzz_ReleaseOrMint_Success(address recipient, uint256 amount) public { + // Since the owner already has tokens this would break the checks + vm.assume(recipient != OWNER); + vm.assume(recipient != address(0)); + vm.assume(recipient != address(s_token)); + + // Makes sure the pool always has enough funds + deal(address(s_token), address(s_lockReleaseTokenPool), amount); + vm.startPrank(s_allowedOffRamp); + + uint256 capacity = _getInboundRateLimiterConfig().capacity; + // Determine if we hit the rate limit or the txs should succeed. + if (amount > capacity) { + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_token)) + ); + } else { + // Only rate limit if the amount is >0 + if (amount > 0) { + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + } + + vm.expectEmit(); + emit TokenPool.Released(s_allowedOffRamp, recipient, amount); + } + + s_lockReleaseTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: bytes(""), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: abi.encode(s_sourcePoolAddress), + sourcePoolData: "", + offchainTokenData: "" + }) + ); + } + + function test_ChainNotAllowed_Revert() public { + address notAllowedRemotePoolAddress = address(1); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(notAllowedRemotePoolAddress), + remoteTokenAddress: abi.encode(address(2)), + allowed: false, + outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), + inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) + }); + + s_lockReleaseTokenPool.applyChainUpdates(chainUpdate); + + vm.startPrank(s_allowedOffRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, SOURCE_CHAIN_SELECTOR)); + s_lockReleaseTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: bytes(""), + receiver: OWNER, + amount: 1e5, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: abi.encode(s_sourcePoolAddress), + sourcePoolData: "", + offchainTokenData: "" + }) + ); + } + + function test_PoolMintNotHealthy_Revert() public { + // Should not mint tokens if cursed. + s_mockRMN.setGlobalCursed(true); + uint256 before = s_token.balanceOf(OWNER); + vm.startPrank(s_allowedOffRamp); + vm.expectRevert(TokenPool.CursedByRMN.selector); + s_lockReleaseTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: bytes(""), + receiver: OWNER, + amount: 1e5, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: _generateSourceTokenData().sourcePoolAddress, + sourcePoolData: _generateSourceTokenData().extraData, + offchainTokenData: "" + }) + ); + + assertEq(s_token.balanceOf(OWNER), before); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.setRebalancer.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.setRebalancer.t.sol new file mode 100644 index 00000000000..25286c1a376 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.setRebalancer.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../shared/access/Ownable2Step.sol"; +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +contract LockReleaseTokenPool_setRebalancer is LockReleaseTokenPoolSetup { + function test_SetRebalancer_Success() public { + assertEq(address(s_lockReleaseTokenPool.getRebalancer()), OWNER); + s_lockReleaseTokenPool.setRebalancer(STRANGER); + assertEq(address(s_lockReleaseTokenPool.getRebalancer()), STRANGER); + } + + function test_SetRebalancer_Revert() public { + vm.startPrank(STRANGER); + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + s_lockReleaseTokenPool.setRebalancer(STRANGER); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.supportsInterface.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.supportsInterface.t.sol new file mode 100644 index 00000000000..9a08fc38c96 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.supportsInterface.t.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IPoolV1} from "../../../interfaces/IPool.sol"; + +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +import {IERC165} from "../../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol"; + +contract LockReleaseTokenPool_supportsInterface is LockReleaseTokenPoolSetup { + function test_SupportsInterface_Success() public view { + assertTrue(s_lockReleaseTokenPool.supportsInterface(type(IPoolV1).interfaceId)); + assertTrue(s_lockReleaseTokenPool.supportsInterface(type(IERC165).interfaceId)); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.transferLiquidity.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.transferLiquidity.t.sol new file mode 100644 index 00000000000..bd2636dbb98 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.transferLiquidity.t.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +contract LockReleaseTokenPool_transferLiquidity is LockReleaseTokenPoolSetup { + LockReleaseTokenPool internal s_oldLockReleaseTokenPool; + uint256 internal s_amount = 100000; + + function setUp() public virtual override { + super.setUp(); + + s_oldLockReleaseTokenPool = + new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), true, address(s_sourceRouter)); + + deal(address(s_token), address(s_oldLockReleaseTokenPool), s_amount); + } + + function test_transferLiquidity_Success() public { + uint256 balancePre = s_token.balanceOf(address(s_lockReleaseTokenPool)); + + s_oldLockReleaseTokenPool.setRebalancer(address(s_lockReleaseTokenPool)); + + vm.expectEmit(); + emit LockReleaseTokenPool.LiquidityTransferred(address(s_oldLockReleaseTokenPool), s_amount); + + s_lockReleaseTokenPool.transferLiquidity(address(s_oldLockReleaseTokenPool), s_amount); + + assertEq(s_token.balanceOf(address(s_lockReleaseTokenPool)), balancePre + s_amount); + } + + function test_transferLiquidity_transferTooMuch_Revert() public { + uint256 balancePre = s_token.balanceOf(address(s_lockReleaseTokenPool)); + + s_oldLockReleaseTokenPool.setRebalancer(address(s_lockReleaseTokenPool)); + + vm.expectRevert(LockReleaseTokenPool.InsufficientLiquidity.selector); + s_lockReleaseTokenPool.transferLiquidity(address(s_oldLockReleaseTokenPool), s_amount + 1); + + assertEq(s_token.balanceOf(address(s_lockReleaseTokenPool)), balancePre); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.withdrawalLiquidity.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.withdrawalLiquidity.t.sol new file mode 100644 index 00000000000..0a2b7b28b0f --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPool.withdrawalLiquidity.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {LockReleaseTokenPoolSetup} from "./LockReleaseTokenPoolSetup.t.sol"; + +contract LockReleaseTokenPool_withdrawalLiquidity is LockReleaseTokenPoolSetup { + function test_Fuzz_WithdrawalLiquidity_Success( + uint256 amount + ) public { + uint256 balancePre = s_token.balanceOf(OWNER); + s_token.approve(address(s_lockReleaseTokenPool), amount); + s_lockReleaseTokenPool.provideLiquidity(amount); + + s_lockReleaseTokenPool.withdrawLiquidity(amount); + + assertEq(s_token.balanceOf(OWNER), balancePre); + } + + // Reverts + function test_Unauthorized_Revert() public { + vm.startPrank(STRANGER); + vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); + + s_lockReleaseTokenPool.withdrawLiquidity(1); + } + + function test_InsufficientLiquidity_Revert() public { + uint256 maxUint256 = 2 ** 256 - 1; + s_token.approve(address(s_lockReleaseTokenPool), maxUint256); + s_lockReleaseTokenPool.provideLiquidity(maxUint256); + + vm.startPrank(address(s_lockReleaseTokenPool)); + s_token.transfer(OWNER, maxUint256); + vm.startPrank(OWNER); + + vm.expectRevert(LockReleaseTokenPool.InsufficientLiquidity.selector); + s_lockReleaseTokenPool.withdrawLiquidity(1); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPoolSetup.t.sol b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPoolSetup.t.sol new file mode 100644 index 00000000000..ce1104246dd --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/LockReleaseTokenPool/LockReleaseTokenPoolSetup.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {BurnMintERC677} from "../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../Router.sol"; +import {LockReleaseTokenPool} from "../../../pools/LockReleaseTokenPool.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {RouterSetup} from "../../router/RouterSetup.t.sol"; + +contract LockReleaseTokenPoolSetup is RouterSetup { + IERC20 internal s_token; + LockReleaseTokenPool internal s_lockReleaseTokenPool; + LockReleaseTokenPool internal s_lockReleaseTokenPoolWithAllowList; + address[] internal s_allowedList; + + address internal s_allowedOnRamp = address(123); + address internal s_allowedOffRamp = address(234); + + address internal s_destPoolAddress = address(2736782345); + address internal s_sourcePoolAddress = address(53852352095); + + function setUp() public virtual override { + RouterSetup.setUp(); + s_token = new BurnMintERC677("LINK", "LNK", 18, 0); + deal(address(s_token), OWNER, type(uint256).max); + s_lockReleaseTokenPool = + new LockReleaseTokenPool(s_token, new address[](0), address(s_mockRMN), true, address(s_sourceRouter)); + + s_allowedList.push(USER_1); + s_allowedList.push(OWNER); + s_lockReleaseTokenPoolWithAllowList = + new LockReleaseTokenPool(s_token, s_allowedList, address(s_mockRMN), true, address(s_sourceRouter)); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(s_destPoolAddress), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_lockReleaseTokenPool.applyChainUpdates(chainUpdate); + s_lockReleaseTokenPoolWithAllowList.applyChainUpdates(chainUpdate); + s_lockReleaseTokenPool.setRebalancer(OWNER); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_allowedOnRamp}); + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: s_allowedOffRamp}); + s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool.t.sol deleted file mode 100644 index 9622f011d61..00000000000 --- a/contracts/src/v0.8/ccip/test/pools/TokenPool.t.sol +++ /dev/null @@ -1,786 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {Router} from "../../Router.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {TokenPoolHelper} from "../helpers/TokenPoolHelper.sol"; -import {RouterSetup} from "../router/RouterSetup.t.sol"; - -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; - -contract TokenPoolSetup is RouterSetup { - IERC20 internal s_token; - TokenPoolHelper internal s_tokenPool; - - function setUp() public virtual override { - RouterSetup.setUp(); - s_token = new BurnMintERC677("LINK", "LNK", 18, 0); - deal(address(s_token), OWNER, type(uint256).max); - - s_tokenPool = new TokenPoolHelper(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); - } -} - -contract TokenPool_constructor is TokenPoolSetup { - function test_immutableFields_Success() public view { - assertEq(address(s_token), address(s_tokenPool.getToken())); - assertEq(address(s_mockRMN), s_tokenPool.getRmnProxy()); - assertEq(false, s_tokenPool.getAllowListEnabled()); - assertEq(address(s_sourceRouter), s_tokenPool.getRouter()); - } - - // Reverts - function test_ZeroAddressNotAllowed_Revert() public { - vm.expectRevert(TokenPool.ZeroAddressNotAllowed.selector); - - s_tokenPool = new TokenPoolHelper(IERC20(address(0)), new address[](0), address(s_mockRMN), address(s_sourceRouter)); - } -} - -contract TokenPool_getRemotePool is TokenPoolSetup { - function test_getRemotePool_Success() public { - uint64 chainSelector = 123124; - address remotePool = makeAddr("remotePool"); - address remoteToken = makeAddr("remoteToken"); - - // Zero indicates nothing is set - assertEq(0, s_tokenPool.getRemotePool(chainSelector).length); - - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(remotePool), - remoteTokenAddress: abi.encode(remoteToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdates); - - assertEq(remotePool, abi.decode(s_tokenPool.getRemotePool(chainSelector), (address))); - } -} - -contract TokenPool_setRemotePool is TokenPoolSetup { - function test_setRemotePool_Success() public { - uint64 chainSelector = DEST_CHAIN_SELECTOR; - address initialPool = makeAddr("remotePool"); - address remoteToken = makeAddr("remoteToken"); - // The new pool is a non-evm pool, as it doesn't fit in the normal 160 bits - bytes memory newPool = abi.encode(type(uint256).max); - - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(initialPool), - remoteTokenAddress: abi.encode(remoteToken), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdates); - - vm.expectEmit(); - emit TokenPool.RemotePoolSet(chainSelector, abi.encode(initialPool), newPool); - - s_tokenPool.setRemotePool(chainSelector, newPool); - - assertEq(keccak256(newPool), keccak256(s_tokenPool.getRemotePool(chainSelector))); - } - - // Reverts - - function test_setRemotePool_NonExistentChain_Reverts() public { - uint64 chainSelector = 123124; - bytes memory remotePool = abi.encode(makeAddr("remotePool")); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, chainSelector)); - s_tokenPool.setRemotePool(chainSelector, remotePool); - } - - function test_setRemotePool_OnlyOwner_Reverts() public { - vm.startPrank(STRANGER); - - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - s_tokenPool.setRemotePool(123124, abi.encode(makeAddr("remotePool"))); - } -} - -contract TokenPool_applyChainUpdates is TokenPoolSetup { - function assertState( - TokenPool.ChainUpdate[] memory chainUpdates - ) public view { - uint64[] memory chainSelectors = s_tokenPool.getSupportedChains(); - for (uint256 i = 0; i < chainUpdates.length; i++) { - assertEq(chainUpdates[i].remoteChainSelector, chainSelectors[i]); - } - - for (uint256 i = 0; i < chainUpdates.length; ++i) { - assertTrue(s_tokenPool.isSupportedChain(chainUpdates[i].remoteChainSelector)); - RateLimiter.TokenBucket memory bkt = - s_tokenPool.getCurrentOutboundRateLimiterState(chainUpdates[i].remoteChainSelector); - assertEq(bkt.capacity, chainUpdates[i].outboundRateLimiterConfig.capacity); - assertEq(bkt.rate, chainUpdates[i].outboundRateLimiterConfig.rate); - assertEq(bkt.isEnabled, chainUpdates[i].outboundRateLimiterConfig.isEnabled); - - bkt = s_tokenPool.getCurrentInboundRateLimiterState(chainUpdates[i].remoteChainSelector); - assertEq(bkt.capacity, chainUpdates[i].inboundRateLimiterConfig.capacity); - assertEq(bkt.rate, chainUpdates[i].inboundRateLimiterConfig.rate); - assertEq(bkt.isEnabled, chainUpdates[i].inboundRateLimiterConfig.isEnabled); - } - } - - function test_applyChainUpdates_Success() public { - RateLimiter.Config memory outboundRateLimit1 = RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}); - RateLimiter.Config memory inboundRateLimit1 = RateLimiter.Config({isEnabled: true, capacity: 100e29, rate: 1e19}); - RateLimiter.Config memory outboundRateLimit2 = RateLimiter.Config({isEnabled: true, capacity: 100e26, rate: 1e16}); - RateLimiter.Config memory inboundRateLimit2 = RateLimiter.Config({isEnabled: true, capacity: 100e27, rate: 1e17}); - - // EVM chain, which uses the 160 bit evm address space - uint64 evmChainSelector = 1; - bytes memory evmRemotePool = abi.encode(makeAddr("evm_remote_pool")); - bytes memory evmRemoteToken = abi.encode(makeAddr("evm_remote_token")); - - // Non EVM chain, which uses the full 256 bits - uint64 nonEvmChainSelector = type(uint64).max; - bytes memory nonEvmRemotePool = abi.encode(keccak256("non_evm_remote_pool")); - bytes memory nonEvmRemoteToken = abi.encode(keccak256("non_evm_remote_token")); - - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: evmChainSelector, - remotePoolAddress: evmRemotePool, - remoteTokenAddress: evmRemoteToken, - allowed: true, - outboundRateLimiterConfig: outboundRateLimit1, - inboundRateLimiterConfig: inboundRateLimit1 - }); - chainUpdates[1] = TokenPool.ChainUpdate({ - remoteChainSelector: nonEvmChainSelector, - remotePoolAddress: nonEvmRemotePool, - remoteTokenAddress: nonEvmRemoteToken, - allowed: true, - outboundRateLimiterConfig: outboundRateLimit2, - inboundRateLimiterConfig: inboundRateLimit2 - }); - - // Assert configuration is applied - vm.expectEmit(); - emit TokenPool.ChainAdded( - chainUpdates[0].remoteChainSelector, - chainUpdates[0].remoteTokenAddress, - chainUpdates[0].outboundRateLimiterConfig, - chainUpdates[0].inboundRateLimiterConfig - ); - vm.expectEmit(); - emit TokenPool.ChainAdded( - chainUpdates[1].remoteChainSelector, - chainUpdates[1].remoteTokenAddress, - chainUpdates[1].outboundRateLimiterConfig, - chainUpdates[1].inboundRateLimiterConfig - ); - s_tokenPool.applyChainUpdates(chainUpdates); - // on1: rateLimit1, on2: rateLimit2, off1: rateLimit1, off2: rateLimit3 - assertState(chainUpdates); - - // Removing an non-existent chain should revert - TokenPool.ChainUpdate[] memory chainRemoves = new TokenPool.ChainUpdate[](1); - uint64 strangerChainSelector = 120938; - chainRemoves[0] = TokenPool.ChainUpdate({ - remoteChainSelector: strangerChainSelector, - remotePoolAddress: evmRemotePool, - remoteTokenAddress: evmRemoteToken, - allowed: false, - outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), - inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) - }); - vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, strangerChainSelector)); - s_tokenPool.applyChainUpdates(chainRemoves); - // State remains - assertState(chainUpdates); - - // Can remove a chain - chainRemoves[0].remoteChainSelector = evmChainSelector; - - vm.expectEmit(); - emit TokenPool.ChainRemoved(chainRemoves[0].remoteChainSelector); - - s_tokenPool.applyChainUpdates(chainRemoves); - - // State updated, only chain 2 remains - TokenPool.ChainUpdate[] memory singleChainConfigured = new TokenPool.ChainUpdate[](1); - singleChainConfigured[0] = chainUpdates[1]; - assertState(singleChainConfigured); - - // Cannot reset already configured ramp - vm.expectRevert( - abi.encodeWithSelector(TokenPool.ChainAlreadyExists.selector, singleChainConfigured[0].remoteChainSelector) - ); - s_tokenPool.applyChainUpdates(singleChainConfigured); - } - - // Reverts - - function test_applyChainUpdates_OnlyCallableByOwner_Revert() public { - vm.startPrank(STRANGER); - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - s_tokenPool.applyChainUpdates(new TokenPool.ChainUpdate[](0)); - } - - function test_applyChainUpdates_ZeroAddressNotAllowed_Revert() public { - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: 1, - remotePoolAddress: "", - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}), - inboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}) - }); - - vm.expectRevert(TokenPool.ZeroAddressNotAllowed.selector); - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: 1, - remotePoolAddress: abi.encode(address(2)), - remoteTokenAddress: "", - allowed: true, - outboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}), - inboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}) - }); - - vm.expectRevert(TokenPool.ZeroAddressNotAllowed.selector); - s_tokenPool.applyChainUpdates(chainUpdates); - } - - function test_applyChainUpdates_DisabledNonZeroRateLimit_Revert() public { - RateLimiter.Config memory outboundRateLimit = RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}); - RateLimiter.Config memory inboundRateLimit = RateLimiter.Config({isEnabled: true, capacity: 100e22, rate: 1e12}); - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: 1, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: outboundRateLimit, - inboundRateLimiterConfig: inboundRateLimit - }); - - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates[0].allowed = false; - chainUpdates[0].outboundRateLimiterConfig = RateLimiter.Config({isEnabled: false, capacity: 10, rate: 1}); - chainUpdates[0].inboundRateLimiterConfig = RateLimiter.Config({isEnabled: false, capacity: 10, rate: 1}); - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.DisabledNonZeroRateLimit.selector, chainUpdates[0].outboundRateLimiterConfig) - ); - s_tokenPool.applyChainUpdates(chainUpdates); - } - - function test_applyChainUpdates_NonExistentChain_Revert() public { - RateLimiter.Config memory outboundRateLimit = RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}); - RateLimiter.Config memory inboundRateLimit = RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}); - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: 1, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: false, - outboundRateLimiterConfig: outboundRateLimit, - inboundRateLimiterConfig: inboundRateLimit - }); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, chainUpdates[0].remoteChainSelector)); - s_tokenPool.applyChainUpdates(chainUpdates); - } - - function test_applyChainUpdates_InvalidRateLimitRate_Revert() public { - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: 1, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 0, rate: 0}), - inboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e22, rate: 1e12}) - }); - - // Outbound - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].outboundRateLimiterConfig) - ); - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates[0].outboundRateLimiterConfig.rate = 100; - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].outboundRateLimiterConfig) - ); - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates[0].outboundRateLimiterConfig.capacity = 100; - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].outboundRateLimiterConfig) - ); - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates[0].outboundRateLimiterConfig.capacity = 101; - - s_tokenPool.applyChainUpdates(chainUpdates); - - // Change the chain selector as adding the same one would revert - chainUpdates[0].remoteChainSelector = 2; - - // Inbound - - chainUpdates[0].inboundRateLimiterConfig.capacity = 0; - chainUpdates[0].inboundRateLimiterConfig.rate = 0; - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].inboundRateLimiterConfig) - ); - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates[0].inboundRateLimiterConfig.rate = 100; - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].inboundRateLimiterConfig) - ); - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates[0].inboundRateLimiterConfig.capacity = 100; - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].inboundRateLimiterConfig) - ); - s_tokenPool.applyChainUpdates(chainUpdates); - - chainUpdates[0].inboundRateLimiterConfig.capacity = 101; - - s_tokenPool.applyChainUpdates(chainUpdates); - } -} - -contract TokenPool_setChainRateLimiterConfig is TokenPoolSetup { - uint64 internal s_remoteChainSelector; - - function setUp() public virtual override { - TokenPoolSetup.setUp(); - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - s_remoteChainSelector = 123124; - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: s_remoteChainSelector, - remotePoolAddress: abi.encode(address(2)), - remoteTokenAddress: abi.encode(address(3)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdates); - } - - function test_Fuzz_SetChainRateLimiterConfig_Success(uint128 capacity, uint128 rate, uint32 newTime) public { - // Cap the lower bound to 4 so 4/2 is still >= 2 - vm.assume(capacity >= 4); - // Cap the lower bound to 2 so 2/2 is still >= 1 - rate = uint128(bound(rate, 2, capacity - 2)); - // Bucket updates only work on increasing time - newTime = uint32(bound(newTime, block.timestamp + 1, type(uint32).max)); - vm.warp(newTime); - - uint256 oldOutboundTokens = s_tokenPool.getCurrentOutboundRateLimiterState(s_remoteChainSelector).tokens; - uint256 oldInboundTokens = s_tokenPool.getCurrentInboundRateLimiterState(s_remoteChainSelector).tokens; - - RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({isEnabled: true, capacity: capacity, rate: rate}); - RateLimiter.Config memory newInboundConfig = - RateLimiter.Config({isEnabled: true, capacity: capacity / 2, rate: rate / 2}); - - vm.expectEmit(); - emit RateLimiter.ConfigChanged(newOutboundConfig); - vm.expectEmit(); - emit RateLimiter.ConfigChanged(newInboundConfig); - vm.expectEmit(); - emit TokenPool.ChainConfigured(s_remoteChainSelector, newOutboundConfig, newInboundConfig); - - s_tokenPool.setChainRateLimiterConfig(s_remoteChainSelector, newOutboundConfig, newInboundConfig); - - uint256 expectedTokens = RateLimiter._min(newOutboundConfig.capacity, oldOutboundTokens); - - RateLimiter.TokenBucket memory bucket = s_tokenPool.getCurrentOutboundRateLimiterState(s_remoteChainSelector); - assertEq(bucket.capacity, newOutboundConfig.capacity); - assertEq(bucket.rate, newOutboundConfig.rate); - assertEq(bucket.tokens, expectedTokens); - assertEq(bucket.lastUpdated, newTime); - - expectedTokens = RateLimiter._min(newInboundConfig.capacity, oldInboundTokens); - - bucket = s_tokenPool.getCurrentInboundRateLimiterState(s_remoteChainSelector); - assertEq(bucket.capacity, newInboundConfig.capacity); - assertEq(bucket.rate, newInboundConfig.rate); - assertEq(bucket.tokens, expectedTokens); - assertEq(bucket.lastUpdated, newTime); - } - - // Reverts - - function test_OnlyOwnerOrRateLimitAdmin_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); - s_tokenPool.setChainRateLimiterConfig( - s_remoteChainSelector, _getOutboundRateLimiterConfig(), _getInboundRateLimiterConfig() - ); - } - - function test_NonExistentChain_Revert() public { - uint64 wrongChainSelector = 9084102894; - - vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, wrongChainSelector)); - s_tokenPool.setChainRateLimiterConfig( - wrongChainSelector, _getOutboundRateLimiterConfig(), _getInboundRateLimiterConfig() - ); - } -} - -contract LockRelease_setRateLimitAdmin is TokenPoolSetup { - function test_SetRateLimitAdmin_Success() public { - assertEq(address(0), s_tokenPool.getRateLimitAdmin()); - s_tokenPool.setRateLimitAdmin(OWNER); - assertEq(OWNER, s_tokenPool.getRateLimitAdmin()); - } - - // Reverts - - function test_SetRateLimitAdmin_Revert() public { - vm.startPrank(STRANGER); - - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - s_tokenPool.setRateLimitAdmin(STRANGER); - } -} - -contract TokenPool_onlyOnRamp is TokenPoolSetup { - function test_onlyOnRamp_Success() public { - uint64 chainSelector = 13377; - address onRamp = makeAddr("onRamp"); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdate); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: chainSelector, onRamp: onRamp}); - s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); - - vm.startPrank(onRamp); - - s_tokenPool.onlyOnRampModifier(chainSelector); - } - - function test_ChainNotAllowed_Revert() public { - uint64 chainSelector = 13377; - address onRamp = makeAddr("onRamp"); - - vm.startPrank(onRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); - s_tokenPool.onlyOnRampModifier(chainSelector); - - vm.startPrank(OWNER); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdate); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: chainSelector, onRamp: onRamp}); - s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); - - vm.startPrank(onRamp); - // Should succeed now that we've added the chain - s_tokenPool.onlyOnRampModifier(chainSelector); - - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: false, - outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), - inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) - }); - - vm.startPrank(OWNER); - s_tokenPool.applyChainUpdates(chainUpdate); - - vm.startPrank(onRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); - s_tokenPool.onlyOffRampModifier(chainSelector); - } - - function test_CallerIsNotARampOnRouter_Revert() public { - uint64 chainSelector = 13377; - address onRamp = makeAddr("onRamp"); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdate); - - vm.startPrank(onRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.CallerIsNotARampOnRouter.selector, onRamp)); - - s_tokenPool.onlyOnRampModifier(chainSelector); - } -} - -contract TokenPool_onlyOffRamp is TokenPoolSetup { - function test_onlyOffRamp_Success() public { - uint64 chainSelector = 13377; - address offRamp = makeAddr("onRamp"); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdate); - - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: chainSelector, offRamp: offRamp}); - s_sourceRouter.applyRampUpdates(new Router.OnRamp[](0), new Router.OffRamp[](0), offRampUpdates); - - vm.startPrank(offRamp); - - s_tokenPool.onlyOffRampModifier(chainSelector); - } - - function test_ChainNotAllowed_Revert() public { - uint64 chainSelector = 13377; - address offRamp = makeAddr("onRamp"); - - vm.startPrank(offRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); - s_tokenPool.onlyOffRampModifier(chainSelector); - - vm.startPrank(OWNER); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdate); - - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: chainSelector, offRamp: offRamp}); - s_sourceRouter.applyRampUpdates(new Router.OnRamp[](0), new Router.OffRamp[](0), offRampUpdates); - - vm.startPrank(offRamp); - // Should succeed now that we've added the chain - s_tokenPool.onlyOffRampModifier(chainSelector); - - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: false, - outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), - inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) - }); - - vm.startPrank(OWNER); - s_tokenPool.applyChainUpdates(chainUpdate); - - vm.startPrank(offRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); - s_tokenPool.onlyOffRampModifier(chainSelector); - } - - function test_CallerIsNotARampOnRouter_Revert() public { - uint64 chainSelector = 13377; - address offRamp = makeAddr("offRamp"); - - TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); - chainUpdate[0] = TokenPool.ChainUpdate({ - remoteChainSelector: chainSelector, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - s_tokenPool.applyChainUpdates(chainUpdate); - - vm.startPrank(offRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.CallerIsNotARampOnRouter.selector, offRamp)); - - s_tokenPool.onlyOffRampModifier(chainSelector); - } -} - -contract TokenPoolWithAllowListSetup is TokenPoolSetup { - address[] internal s_allowedSenders; - - function setUp() public virtual override { - TokenPoolSetup.setUp(); - - s_allowedSenders.push(STRANGER); - s_allowedSenders.push(DUMMY_CONTRACT_ADDRESS); - - s_tokenPool = new TokenPoolHelper(s_token, s_allowedSenders, address(s_mockRMN), address(s_sourceRouter)); - } -} - -contract TokenPoolWithAllowList_getAllowListEnabled is TokenPoolWithAllowListSetup { - function test_GetAllowListEnabled_Success() public view { - assertTrue(s_tokenPool.getAllowListEnabled()); - } -} - -contract TokenPoolWithAllowList_setRouter is TokenPoolWithAllowListSetup { - function test_SetRouter_Success() public { - assertEq(address(s_sourceRouter), s_tokenPool.getRouter()); - - address newRouter = makeAddr("newRouter"); - - vm.expectEmit(); - emit TokenPool.RouterUpdated(address(s_sourceRouter), newRouter); - - s_tokenPool.setRouter(newRouter); - - assertEq(newRouter, s_tokenPool.getRouter()); - } -} - -contract TokenPoolWithAllowList_getAllowList is TokenPoolWithAllowListSetup { - function test_GetAllowList_Success() public view { - address[] memory setAddresses = s_tokenPool.getAllowList(); - assertEq(2, setAddresses.length); - assertEq(s_allowedSenders[0], setAddresses[0]); - assertEq(s_allowedSenders[1], setAddresses[1]); - } -} - -contract TokenPoolWithAllowList_applyAllowListUpdates is TokenPoolWithAllowListSetup { - function test_SetAllowList_Success() public { - address[] memory newAddresses = new address[](2); - newAddresses[0] = address(1); - newAddresses[1] = address(2); - - for (uint256 i = 0; i < 2; ++i) { - vm.expectEmit(); - emit TokenPool.AllowListAdd(newAddresses[i]); - } - - s_tokenPool.applyAllowListUpdates(new address[](0), newAddresses); - address[] memory setAddresses = s_tokenPool.getAllowList(); - - assertEq(s_allowedSenders[0], setAddresses[0]); - assertEq(s_allowedSenders[1], setAddresses[1]); - assertEq(address(1), setAddresses[2]); - assertEq(address(2), setAddresses[3]); - - // address(2) exists noop, add address(3), remove address(1) - newAddresses = new address[](2); - newAddresses[0] = address(2); - newAddresses[1] = address(3); - - address[] memory removeAddresses = new address[](1); - removeAddresses[0] = address(1); - - vm.expectEmit(); - emit TokenPool.AllowListRemove(address(1)); - - vm.expectEmit(); - emit TokenPool.AllowListAdd(address(3)); - - s_tokenPool.applyAllowListUpdates(removeAddresses, newAddresses); - setAddresses = s_tokenPool.getAllowList(); - - assertEq(s_allowedSenders[0], setAddresses[0]); - assertEq(s_allowedSenders[1], setAddresses[1]); - assertEq(address(2), setAddresses[2]); - assertEq(address(3), setAddresses[3]); - - // remove all from allowlist - for (uint256 i = 0; i < setAddresses.length; ++i) { - vm.expectEmit(); - emit TokenPool.AllowListRemove(setAddresses[i]); - } - - s_tokenPool.applyAllowListUpdates(setAddresses, new address[](0)); - setAddresses = s_tokenPool.getAllowList(); - - assertEq(0, setAddresses.length); - } - - function test_SetAllowListSkipsZero_Success() public { - uint256 setAddressesLength = s_tokenPool.getAllowList().length; - - address[] memory newAddresses = new address[](1); - newAddresses[0] = address(0); - - s_tokenPool.applyAllowListUpdates(new address[](0), newAddresses); - address[] memory setAddresses = s_tokenPool.getAllowList(); - - assertEq(setAddresses.length, setAddressesLength); - } - - // Reverts - - function test_OnlyOwner_Revert() public { - vm.stopPrank(); - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - address[] memory newAddresses = new address[](2); - s_tokenPool.applyAllowListUpdates(new address[](0), newAddresses); - } - - function test_AllowListNotEnabled_Revert() public { - s_tokenPool = new TokenPoolHelper(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); - - vm.expectRevert(TokenPool.AllowListNotEnabled.selector); - - s_tokenPool.applyAllowListUpdates(new address[](0), new address[](2)); - } -} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.applyAllowListUpdates.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.applyAllowListUpdates.t.sol new file mode 100644 index 00000000000..2862b8c71ae --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.applyAllowListUpdates.t.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../shared/access/Ownable2Step.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolHelper} from "../../helpers/TokenPoolHelper.sol"; + +import {TokenPoolWithAllowListSetup} from "./TokenPoolWithAllowListSetup.t.sol"; + +contract TokenPoolWithAllowList_applyAllowListUpdates is TokenPoolWithAllowListSetup { + function test_SetAllowList_Success() public { + address[] memory newAddresses = new address[](2); + newAddresses[0] = address(1); + newAddresses[1] = address(2); + + for (uint256 i = 0; i < 2; ++i) { + vm.expectEmit(); + emit TokenPool.AllowListAdd(newAddresses[i]); + } + + s_tokenPool.applyAllowListUpdates(new address[](0), newAddresses); + address[] memory setAddresses = s_tokenPool.getAllowList(); + + assertEq(s_allowedSenders[0], setAddresses[0]); + assertEq(s_allowedSenders[1], setAddresses[1]); + assertEq(address(1), setAddresses[2]); + assertEq(address(2), setAddresses[3]); + + // address(2) exists noop, add address(3), remove address(1) + newAddresses = new address[](2); + newAddresses[0] = address(2); + newAddresses[1] = address(3); + + address[] memory removeAddresses = new address[](1); + removeAddresses[0] = address(1); + + vm.expectEmit(); + emit TokenPool.AllowListRemove(address(1)); + + vm.expectEmit(); + emit TokenPool.AllowListAdd(address(3)); + + s_tokenPool.applyAllowListUpdates(removeAddresses, newAddresses); + setAddresses = s_tokenPool.getAllowList(); + + assertEq(s_allowedSenders[0], setAddresses[0]); + assertEq(s_allowedSenders[1], setAddresses[1]); + assertEq(address(2), setAddresses[2]); + assertEq(address(3), setAddresses[3]); + + // remove all from allowlist + for (uint256 i = 0; i < setAddresses.length; ++i) { + vm.expectEmit(); + emit TokenPool.AllowListRemove(setAddresses[i]); + } + + s_tokenPool.applyAllowListUpdates(setAddresses, new address[](0)); + setAddresses = s_tokenPool.getAllowList(); + + assertEq(0, setAddresses.length); + } + + function test_SetAllowListSkipsZero_Success() public { + uint256 setAddressesLength = s_tokenPool.getAllowList().length; + + address[] memory newAddresses = new address[](1); + newAddresses[0] = address(0); + + s_tokenPool.applyAllowListUpdates(new address[](0), newAddresses); + address[] memory setAddresses = s_tokenPool.getAllowList(); + + assertEq(setAddresses.length, setAddressesLength); + } + + // Reverts + + function test_OnlyOwner_Revert() public { + vm.stopPrank(); + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + address[] memory newAddresses = new address[](2); + s_tokenPool.applyAllowListUpdates(new address[](0), newAddresses); + } + + function test_AllowListNotEnabled_Revert() public { + s_tokenPool = new TokenPoolHelper(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); + + vm.expectRevert(TokenPool.AllowListNotEnabled.selector); + + s_tokenPool.applyAllowListUpdates(new address[](0), new address[](2)); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.applyChainUpdates.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.applyChainUpdates.t.sol new file mode 100644 index 00000000000..a24fa3d0e8a --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.applyChainUpdates.t.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../shared/access/Ownable2Step.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPool_applyChainUpdates is TokenPoolSetup { + function assertState( + TokenPool.ChainUpdate[] memory chainUpdates + ) public view { + uint64[] memory chainSelectors = s_tokenPool.getSupportedChains(); + for (uint256 i = 0; i < chainUpdates.length; i++) { + assertEq(chainUpdates[i].remoteChainSelector, chainSelectors[i]); + } + + for (uint256 i = 0; i < chainUpdates.length; ++i) { + assertTrue(s_tokenPool.isSupportedChain(chainUpdates[i].remoteChainSelector)); + RateLimiter.TokenBucket memory bkt = + s_tokenPool.getCurrentOutboundRateLimiterState(chainUpdates[i].remoteChainSelector); + assertEq(bkt.capacity, chainUpdates[i].outboundRateLimiterConfig.capacity); + assertEq(bkt.rate, chainUpdates[i].outboundRateLimiterConfig.rate); + assertEq(bkt.isEnabled, chainUpdates[i].outboundRateLimiterConfig.isEnabled); + + bkt = s_tokenPool.getCurrentInboundRateLimiterState(chainUpdates[i].remoteChainSelector); + assertEq(bkt.capacity, chainUpdates[i].inboundRateLimiterConfig.capacity); + assertEq(bkt.rate, chainUpdates[i].inboundRateLimiterConfig.rate); + assertEq(bkt.isEnabled, chainUpdates[i].inboundRateLimiterConfig.isEnabled); + } + } + + function test_applyChainUpdates_Success() public { + RateLimiter.Config memory outboundRateLimit1 = RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}); + RateLimiter.Config memory inboundRateLimit1 = RateLimiter.Config({isEnabled: true, capacity: 100e29, rate: 1e19}); + RateLimiter.Config memory outboundRateLimit2 = RateLimiter.Config({isEnabled: true, capacity: 100e26, rate: 1e16}); + RateLimiter.Config memory inboundRateLimit2 = RateLimiter.Config({isEnabled: true, capacity: 100e27, rate: 1e17}); + + // EVM chain, which uses the 160 bit evm address space + uint64 evmChainSelector = 1; + bytes memory evmRemotePool = abi.encode(makeAddr("evm_remote_pool")); + bytes memory evmRemoteToken = abi.encode(makeAddr("evm_remote_token")); + + // Non EVM chain, which uses the full 256 bits + uint64 nonEvmChainSelector = type(uint64).max; + bytes memory nonEvmRemotePool = abi.encode(keccak256("non_evm_remote_pool")); + bytes memory nonEvmRemoteToken = abi.encode(keccak256("non_evm_remote_token")); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: evmChainSelector, + remotePoolAddress: evmRemotePool, + remoteTokenAddress: evmRemoteToken, + allowed: true, + outboundRateLimiterConfig: outboundRateLimit1, + inboundRateLimiterConfig: inboundRateLimit1 + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: nonEvmChainSelector, + remotePoolAddress: nonEvmRemotePool, + remoteTokenAddress: nonEvmRemoteToken, + allowed: true, + outboundRateLimiterConfig: outboundRateLimit2, + inboundRateLimiterConfig: inboundRateLimit2 + }); + + // Assert configuration is applied + vm.expectEmit(); + emit TokenPool.ChainAdded( + chainUpdates[0].remoteChainSelector, + chainUpdates[0].remoteTokenAddress, + chainUpdates[0].outboundRateLimiterConfig, + chainUpdates[0].inboundRateLimiterConfig + ); + vm.expectEmit(); + emit TokenPool.ChainAdded( + chainUpdates[1].remoteChainSelector, + chainUpdates[1].remoteTokenAddress, + chainUpdates[1].outboundRateLimiterConfig, + chainUpdates[1].inboundRateLimiterConfig + ); + s_tokenPool.applyChainUpdates(chainUpdates); + // on1: rateLimit1, on2: rateLimit2, off1: rateLimit1, off2: rateLimit3 + assertState(chainUpdates); + + // Removing an non-existent chain should revert + TokenPool.ChainUpdate[] memory chainRemoves = new TokenPool.ChainUpdate[](1); + uint64 strangerChainSelector = 120938; + chainRemoves[0] = TokenPool.ChainUpdate({ + remoteChainSelector: strangerChainSelector, + remotePoolAddress: evmRemotePool, + remoteTokenAddress: evmRemoteToken, + allowed: false, + outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), + inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) + }); + vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, strangerChainSelector)); + s_tokenPool.applyChainUpdates(chainRemoves); + // State remains + assertState(chainUpdates); + + // Can remove a chain + chainRemoves[0].remoteChainSelector = evmChainSelector; + + vm.expectEmit(); + emit TokenPool.ChainRemoved(chainRemoves[0].remoteChainSelector); + + s_tokenPool.applyChainUpdates(chainRemoves); + + // State updated, only chain 2 remains + TokenPool.ChainUpdate[] memory singleChainConfigured = new TokenPool.ChainUpdate[](1); + singleChainConfigured[0] = chainUpdates[1]; + assertState(singleChainConfigured); + + // Cannot reset already configured ramp + vm.expectRevert( + abi.encodeWithSelector(TokenPool.ChainAlreadyExists.selector, singleChainConfigured[0].remoteChainSelector) + ); + s_tokenPool.applyChainUpdates(singleChainConfigured); + } + + // Reverts + + function test_applyChainUpdates_OnlyCallableByOwner_Revert() public { + vm.startPrank(STRANGER); + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + s_tokenPool.applyChainUpdates(new TokenPool.ChainUpdate[](0)); + } + + function test_applyChainUpdates_ZeroAddressNotAllowed_Revert() public { + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: 1, + remotePoolAddress: "", + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}), + inboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}) + }); + + vm.expectRevert(TokenPool.ZeroAddressNotAllowed.selector); + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: 1, + remotePoolAddress: abi.encode(address(2)), + remoteTokenAddress: "", + allowed: true, + outboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}), + inboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}) + }); + + vm.expectRevert(TokenPool.ZeroAddressNotAllowed.selector); + s_tokenPool.applyChainUpdates(chainUpdates); + } + + function test_applyChainUpdates_DisabledNonZeroRateLimit_Revert() public { + RateLimiter.Config memory outboundRateLimit = RateLimiter.Config({isEnabled: true, capacity: 100e28, rate: 1e18}); + RateLimiter.Config memory inboundRateLimit = RateLimiter.Config({isEnabled: true, capacity: 100e22, rate: 1e12}); + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: 1, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: outboundRateLimit, + inboundRateLimiterConfig: inboundRateLimit + }); + + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates[0].allowed = false; + chainUpdates[0].outboundRateLimiterConfig = RateLimiter.Config({isEnabled: false, capacity: 10, rate: 1}); + chainUpdates[0].inboundRateLimiterConfig = RateLimiter.Config({isEnabled: false, capacity: 10, rate: 1}); + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.DisabledNonZeroRateLimit.selector, chainUpdates[0].outboundRateLimiterConfig) + ); + s_tokenPool.applyChainUpdates(chainUpdates); + } + + function test_applyChainUpdates_NonExistentChain_Revert() public { + RateLimiter.Config memory outboundRateLimit = RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}); + RateLimiter.Config memory inboundRateLimit = RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}); + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: 1, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: false, + outboundRateLimiterConfig: outboundRateLimit, + inboundRateLimiterConfig: inboundRateLimit + }); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, chainUpdates[0].remoteChainSelector)); + s_tokenPool.applyChainUpdates(chainUpdates); + } + + function test_applyChainUpdates_InvalidRateLimitRate_Revert() public { + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: 1, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 0, rate: 0}), + inboundRateLimiterConfig: RateLimiter.Config({isEnabled: true, capacity: 100e22, rate: 1e12}) + }); + + // Outbound + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].outboundRateLimiterConfig) + ); + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates[0].outboundRateLimiterConfig.rate = 100; + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].outboundRateLimiterConfig) + ); + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates[0].outboundRateLimiterConfig.capacity = 100; + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].outboundRateLimiterConfig) + ); + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates[0].outboundRateLimiterConfig.capacity = 101; + + s_tokenPool.applyChainUpdates(chainUpdates); + + // Change the chain selector as adding the same one would revert + chainUpdates[0].remoteChainSelector = 2; + + // Inbound + + chainUpdates[0].inboundRateLimiterConfig.capacity = 0; + chainUpdates[0].inboundRateLimiterConfig.rate = 0; + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].inboundRateLimiterConfig) + ); + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates[0].inboundRateLimiterConfig.rate = 100; + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].inboundRateLimiterConfig) + ); + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates[0].inboundRateLimiterConfig.capacity = 100; + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.InvalidRateLimitRate.selector, chainUpdates[0].inboundRateLimiterConfig) + ); + s_tokenPool.applyChainUpdates(chainUpdates); + + chainUpdates[0].inboundRateLimiterConfig.capacity = 101; + + s_tokenPool.applyChainUpdates(chainUpdates); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.constructor.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.constructor.t.sol new file mode 100644 index 00000000000..cfa0d5b9394 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.constructor.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolHelper} from "../../helpers/TokenPoolHelper.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract TokenPool_constructor is TokenPoolSetup { + function test_immutableFields_Success() public view { + assertEq(address(s_token), address(s_tokenPool.getToken())); + assertEq(address(s_mockRMN), s_tokenPool.getRmnProxy()); + assertEq(false, s_tokenPool.getAllowListEnabled()); + assertEq(address(s_sourceRouter), s_tokenPool.getRouter()); + } + + // Reverts + function test_ZeroAddressNotAllowed_Revert() public { + vm.expectRevert(TokenPool.ZeroAddressNotAllowed.selector); + + s_tokenPool = new TokenPoolHelper(IERC20(address(0)), new address[](0), address(s_mockRMN), address(s_sourceRouter)); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getAllowList.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getAllowList.t.sol new file mode 100644 index 00000000000..8d4256d3479 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getAllowList.t.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {TokenPoolWithAllowListSetup} from "./TokenPoolWithAllowListSetup.t.sol"; + +contract TokenPoolWithAllowList_getAllowList is TokenPoolWithAllowListSetup { + function test_GetAllowList_Success() public view { + address[] memory setAddresses = s_tokenPool.getAllowList(); + assertEq(2, setAddresses.length); + assertEq(s_allowedSenders[0], setAddresses[0]); + assertEq(s_allowedSenders[1], setAddresses[1]); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getAllowListEnabled.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getAllowListEnabled.t.sol new file mode 100644 index 00000000000..2a36a846999 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getAllowListEnabled.t.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {TokenPoolWithAllowListSetup} from "./TokenPoolWithAllowListSetup.t.sol"; + +contract TokenPoolWithAllowList_getAllowListEnabled is TokenPoolWithAllowListSetup { + function test_GetAllowListEnabled_Success() public view { + assertTrue(s_tokenPool.getAllowListEnabled()); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getRemotePool.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getRemotePool.t.sol new file mode 100644 index 00000000000..a3acd8f2690 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.getRemotePool.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPool_getRemotePool is TokenPoolSetup { + function test_getRemotePool_Success() public { + uint64 chainSelector = 123124; + address remotePool = makeAddr("remotePool"); + address remoteToken = makeAddr("remoteToken"); + + // Zero indicates nothing is set + assertEq(0, s_tokenPool.getRemotePool(chainSelector).length); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(remotePool), + remoteTokenAddress: abi.encode(remoteToken), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdates); + + assertEq(remotePool, abi.decode(s_tokenPool.getRemotePool(chainSelector), (address))); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.onlyOffRamp.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.onlyOffRamp.t.sol new file mode 100644 index 00000000000..c514b343d62 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.onlyOffRamp.t.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Router} from "../../../Router.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPool_onlyOffRamp is TokenPoolSetup { + function test_onlyOffRamp_Success() public { + uint64 chainSelector = 13377; + address offRamp = makeAddr("onRamp"); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdate); + + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: chainSelector, offRamp: offRamp}); + s_sourceRouter.applyRampUpdates(new Router.OnRamp[](0), new Router.OffRamp[](0), offRampUpdates); + + vm.startPrank(offRamp); + + s_tokenPool.onlyOffRampModifier(chainSelector); + } + + function test_ChainNotAllowed_Revert() public { + uint64 chainSelector = 13377; + address offRamp = makeAddr("onRamp"); + + vm.startPrank(offRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); + s_tokenPool.onlyOffRampModifier(chainSelector); + + vm.startPrank(OWNER); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdate); + + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: chainSelector, offRamp: offRamp}); + s_sourceRouter.applyRampUpdates(new Router.OnRamp[](0), new Router.OffRamp[](0), offRampUpdates); + + vm.startPrank(offRamp); + // Should succeed now that we've added the chain + s_tokenPool.onlyOffRampModifier(chainSelector); + + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: false, + outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), + inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) + }); + + vm.startPrank(OWNER); + s_tokenPool.applyChainUpdates(chainUpdate); + + vm.startPrank(offRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); + s_tokenPool.onlyOffRampModifier(chainSelector); + } + + function test_CallerIsNotARampOnRouter_Revert() public { + uint64 chainSelector = 13377; + address offRamp = makeAddr("offRamp"); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdate); + + vm.startPrank(offRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.CallerIsNotARampOnRouter.selector, offRamp)); + + s_tokenPool.onlyOffRampModifier(chainSelector); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.onlyOnRamp.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.onlyOnRamp.t.sol new file mode 100644 index 00000000000..5e3e6e31c12 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.onlyOnRamp.t.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Router} from "../../../Router.sol"; +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPool_onlyOnRamp is TokenPoolSetup { + function test_onlyOnRamp_Success() public { + uint64 chainSelector = 13377; + address onRamp = makeAddr("onRamp"); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdate); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: chainSelector, onRamp: onRamp}); + s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); + + vm.startPrank(onRamp); + + s_tokenPool.onlyOnRampModifier(chainSelector); + } + + function test_ChainNotAllowed_Revert() public { + uint64 chainSelector = 13377; + address onRamp = makeAddr("onRamp"); + + vm.startPrank(onRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); + s_tokenPool.onlyOnRampModifier(chainSelector); + + vm.startPrank(OWNER); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdate); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: chainSelector, onRamp: onRamp}); + s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); + + vm.startPrank(onRamp); + // Should succeed now that we've added the chain + s_tokenPool.onlyOnRampModifier(chainSelector); + + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: false, + outboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}), + inboundRateLimiterConfig: RateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}) + }); + + vm.startPrank(OWNER); + s_tokenPool.applyChainUpdates(chainUpdate); + + vm.startPrank(onRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, chainSelector)); + s_tokenPool.onlyOffRampModifier(chainSelector); + } + + function test_CallerIsNotARampOnRouter_Revert() public { + uint64 chainSelector = 13377; + address onRamp = makeAddr("onRamp"); + + TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1); + chainUpdate[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdate); + + vm.startPrank(onRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.CallerIsNotARampOnRouter.selector, onRamp)); + + s_tokenPool.onlyOnRampModifier(chainSelector); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setChainRateLimiterConfig.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setChainRateLimiterConfig.t.sol new file mode 100644 index 00000000000..bee2218a7ff --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setChainRateLimiterConfig.t.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {RateLimiter} from "../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPool_setChainRateLimiterConfig is TokenPoolSetup { + uint64 internal s_remoteChainSelector; + + function setUp() public virtual override { + TokenPoolSetup.setUp(); + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + s_remoteChainSelector = 123124; + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: s_remoteChainSelector, + remotePoolAddress: abi.encode(address(2)), + remoteTokenAddress: abi.encode(address(3)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdates); + } + + function test_Fuzz_SetChainRateLimiterConfig_Success(uint128 capacity, uint128 rate, uint32 newTime) public { + // Cap the lower bound to 4 so 4/2 is still >= 2 + vm.assume(capacity >= 4); + // Cap the lower bound to 2 so 2/2 is still >= 1 + rate = uint128(bound(rate, 2, capacity - 2)); + // Bucket updates only work on increasing time + newTime = uint32(bound(newTime, block.timestamp + 1, type(uint32).max)); + vm.warp(newTime); + + uint256 oldOutboundTokens = s_tokenPool.getCurrentOutboundRateLimiterState(s_remoteChainSelector).tokens; + uint256 oldInboundTokens = s_tokenPool.getCurrentInboundRateLimiterState(s_remoteChainSelector).tokens; + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({isEnabled: true, capacity: capacity, rate: rate}); + RateLimiter.Config memory newInboundConfig = + RateLimiter.Config({isEnabled: true, capacity: capacity / 2, rate: rate / 2}); + + vm.expectEmit(); + emit RateLimiter.ConfigChanged(newOutboundConfig); + vm.expectEmit(); + emit RateLimiter.ConfigChanged(newInboundConfig); + vm.expectEmit(); + emit TokenPool.ChainConfigured(s_remoteChainSelector, newOutboundConfig, newInboundConfig); + + s_tokenPool.setChainRateLimiterConfig(s_remoteChainSelector, newOutboundConfig, newInboundConfig); + + uint256 expectedTokens = RateLimiter._min(newOutboundConfig.capacity, oldOutboundTokens); + + RateLimiter.TokenBucket memory bucket = s_tokenPool.getCurrentOutboundRateLimiterState(s_remoteChainSelector); + assertEq(bucket.capacity, newOutboundConfig.capacity); + assertEq(bucket.rate, newOutboundConfig.rate); + assertEq(bucket.tokens, expectedTokens); + assertEq(bucket.lastUpdated, newTime); + + expectedTokens = RateLimiter._min(newInboundConfig.capacity, oldInboundTokens); + + bucket = s_tokenPool.getCurrentInboundRateLimiterState(s_remoteChainSelector); + assertEq(bucket.capacity, newInboundConfig.capacity); + assertEq(bucket.rate, newInboundConfig.rate); + assertEq(bucket.tokens, expectedTokens); + assertEq(bucket.lastUpdated, newTime); + } + + // Reverts + + function test_OnlyOwnerOrRateLimitAdmin_Revert() public { + vm.startPrank(STRANGER); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, STRANGER)); + s_tokenPool.setChainRateLimiterConfig( + s_remoteChainSelector, _getOutboundRateLimiterConfig(), _getInboundRateLimiterConfig() + ); + } + + function test_NonExistentChain_Revert() public { + uint64 wrongChainSelector = 9084102894; + + vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, wrongChainSelector)); + s_tokenPool.setChainRateLimiterConfig( + wrongChainSelector, _getOutboundRateLimiterConfig(), _getInboundRateLimiterConfig() + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRateLimitAdmin.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRateLimitAdmin.t.sol new file mode 100644 index 00000000000..7c57741cf42 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRateLimitAdmin.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../shared/access/Ownable2Step.sol"; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPool_setRateLimitAdmin is TokenPoolSetup { + function test_SetRateLimitAdmin_Success() public { + assertEq(address(0), s_tokenPool.getRateLimitAdmin()); + vm.expectEmit(); + emit TokenPool.RateLimitAdminSet(OWNER); + s_tokenPool.setRateLimitAdmin(OWNER); + assertEq(OWNER, s_tokenPool.getRateLimitAdmin()); + } + + // Reverts + + function test_SetRateLimitAdmin_Revert() public { + vm.startPrank(STRANGER); + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + s_tokenPool.setRateLimitAdmin(STRANGER); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRemotePool.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRemotePool.t.sol new file mode 100644 index 00000000000..d305e131793 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRemotePool.t.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../shared/access/Ownable2Step.sol"; +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPool_setRemotePool is TokenPoolSetup { + function test_setRemotePool_Success() public { + uint64 chainSelector = DEST_CHAIN_SELECTOR; + address initialPool = makeAddr("remotePool"); + address remoteToken = makeAddr("remoteToken"); + // The new pool is a non-evm pool, as it doesn't fit in the normal 160 bits + bytes memory newPool = abi.encode(type(uint256).max); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: chainSelector, + remotePoolAddress: abi.encode(initialPool), + remoteTokenAddress: abi.encode(remoteToken), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + s_tokenPool.applyChainUpdates(chainUpdates); + + vm.expectEmit(); + emit TokenPool.RemotePoolSet(chainSelector, abi.encode(initialPool), newPool); + + s_tokenPool.setRemotePool(chainSelector, newPool); + + assertEq(keccak256(newPool), keccak256(s_tokenPool.getRemotePool(chainSelector))); + } + + // Reverts + + function test_setRemotePool_NonExistentChain_Reverts() public { + uint64 chainSelector = 123124; + bytes memory remotePool = abi.encode(makeAddr("remotePool")); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.NonExistentChain.selector, chainSelector)); + s_tokenPool.setRemotePool(chainSelector, remotePool); + } + + function test_setRemotePool_OnlyOwner_Reverts() public { + vm.startPrank(STRANGER); + + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + s_tokenPool.setRemotePool(123124, abi.encode(makeAddr("remotePool"))); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRouter.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRouter.t.sol new file mode 100644 index 00000000000..288b7f7081d --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPool.setRouter.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {TokenPool} from "../../../pools/TokenPool.sol"; +import {TokenPoolWithAllowListSetup} from "./TokenPoolWithAllowListSetup.t.sol"; + +contract TokenPoolWithAllowList_setRouter is TokenPoolWithAllowListSetup { + function test_SetRouter_Success() public { + assertEq(address(s_sourceRouter), s_tokenPool.getRouter()); + + address newRouter = makeAddr("newRouter"); + + vm.expectEmit(); + emit TokenPool.RouterUpdated(address(s_sourceRouter), newRouter); + + s_tokenPool.setRouter(newRouter); + + assertEq(newRouter, s_tokenPool.getRouter()); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPoolSetup.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPoolSetup.t.sol new file mode 100644 index 00000000000..e2285c67094 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPoolSetup.t.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {BurnMintERC677} from "../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {TokenPoolHelper} from "../../helpers/TokenPoolHelper.sol"; +import {RouterSetup} from "../../router/RouterSetup.t.sol"; + +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract TokenPoolSetup is RouterSetup { + IERC20 internal s_token; + TokenPoolHelper internal s_tokenPool; + + function setUp() public virtual override { + RouterSetup.setUp(); + s_token = new BurnMintERC677("LINK", "LNK", 18, 0); + deal(address(s_token), OWNER, type(uint256).max); + + s_tokenPool = new TokenPoolHelper(s_token, new address[](0), address(s_mockRMN), address(s_sourceRouter)); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPoolWithAllowListSetup.t.sol b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPoolWithAllowListSetup.t.sol new file mode 100644 index 00000000000..d441c11c352 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/TokenPool/TokenPoolWithAllowListSetup.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {TokenPoolHelper} from "../../helpers/TokenPoolHelper.sol"; +import {TokenPoolSetup} from "./TokenPoolSetup.t.sol"; + +contract TokenPoolWithAllowListSetup is TokenPoolSetup { + address[] internal s_allowedSenders; + + function setUp() public virtual override { + TokenPoolSetup.setUp(); + + s_allowedSenders.push(STRANGER); + s_allowedSenders.push(OWNER); + + s_tokenPool = new TokenPoolHelper(s_token, s_allowedSenders, address(s_mockRMN), address(s_sourceRouter)); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.lockOrBurn.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.lockOrBurn.t.sol new file mode 100644 index 00000000000..b3ee31deade --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.lockOrBurn.t.sol @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; +import {ITokenMessenger} from "../../../../pools/USDC/ITokenMessenger.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; +import {Pool} from "../../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../../libraries/RateLimiter.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract HybridLockReleaseUSDCTokenPool_lockOrBurn is USDCTokenPoolSetup { + function test_onLockReleaseMechanism_Success() public { + bytes32 receiver = bytes32(uint256(uint160(STRANGER))); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + assertTrue( + s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), + "Lock/Release mech not configured for outgoing message to DEST_CHAIN_SELECTOR" + ); + + uint256 amount = 1e6; + + s_token.transfer(address(s_usdcTokenPool), amount); + + vm.startPrank(s_routerAllowedOnRamp); + + vm.expectEmit(); + emit TokenPool.Locked(s_routerAllowedOnRamp, amount); + + s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(receiver), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + + assertEq(s_token.balanceOf(address(s_usdcTokenPool)), amount, "Incorrect token amount in the tokenPool"); + } + + function test_PrimaryMechanism_Success() public { + bytes32 receiver = bytes32(uint256(uint160(STRANGER))); + uint256 amount = 1; + + vm.startPrank(OWNER); + + s_token.transfer(address(s_usdcTokenPool), amount); + + vm.startPrank(s_routerAllowedOnRamp); + + USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPool.getDomain(DEST_CHAIN_SELECTOR); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + + vm.expectEmit(); + emit ITokenMessenger.DepositForBurn( + s_mockUSDC.s_nonce(), + address(s_token), + amount, + address(s_usdcTokenPool), + receiver, + expectedDomain.domainIdentifier, + s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), + expectedDomain.allowedCaller + ); + + vm.expectEmit(); + emit TokenPool.Burned(s_routerAllowedOnRamp, amount); + + Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(receiver), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + + uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); + assertEq(s_mockUSDC.s_nonce() - 1, nonce); + } + + function test_onLockReleaseMechanism_thenswitchToPrimary_Success() public { + // Test Enabling the LR mechanism and sending an outgoing message + test_PrimaryMechanism_Success(); + + // Disable the LR mechanism so that primary CCTP is used and then attempt to send a message + uint64[] memory destChainRemoves = new uint64[](1); + destChainRemoves[0] = DEST_CHAIN_SELECTOR; + + vm.startPrank(OWNER); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LockReleaseDisabled(DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.updateChainSelectorMechanisms(destChainRemoves, new uint64[](0)); + + // Send an outgoing message + test_PrimaryMechanism_Success(); + } + + function test_WhileMigrationPause_Revert() public { + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + // Create a fake migration proposal + s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); + + assertEq(s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), DEST_CHAIN_SELECTOR); + + bytes32 receiver = bytes32(uint256(uint160(STRANGER))); + + assertTrue( + s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), + "Lock Release mech not configured for outgoing message to DEST_CHAIN_SELECTOR" + ); + + uint256 amount = 1e6; + + s_token.transfer(address(s_usdcTokenPool), amount); + + vm.startPrank(s_routerAllowedOnRamp); + + // Expect the lockOrBurn to fail because a pending CCTP-Migration has paused outgoing messages on CCIP + vm.expectRevert( + abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, DEST_CHAIN_SELECTOR) + ); + + s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(receiver), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.releaseOrMint.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.releaseOrMint.t.sol new file mode 100644 index 00000000000..305991aa38f --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.releaseOrMint.t.sol @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; +import {Internal} from "../../../../libraries/Internal.sol"; +import {Pool} from "../../../../libraries/Pool.sol"; +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {LOCK_RELEASE_FLAG} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCBridgeMigrator} from "../../../../pools/USDC/USDCBridgeMigrator.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract HybridLockReleaseUSDCTokenPool_releaseOrMint is USDCTokenPoolSetup { + function test_OnLockReleaseMechanism_Success() public { + address recipient = address(1234); + + // Designate the SOURCE_CHAIN as not using native-USDC, and so the L/R mechanism must be used instead + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = SOURCE_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + assertTrue( + s_usdcTokenPool.shouldUseLockRelease(SOURCE_CHAIN_SELECTOR), + "Lock/Release mech not configured for incoming message from SOURCE_CHAIN_SELECTOR" + ); + + vm.startPrank(OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + + // Add 1e12 liquidity so that there's enough to release + vm.startPrank(s_usdcTokenPool.getLiquidityProvider(SOURCE_CHAIN_SELECTOR)); + + s_token.approve(address(s_usdcTokenPool), type(uint256).max); + + uint256 liquidityAmount = 1e12; + s_usdcTokenPool.provideLiquidity(SOURCE_CHAIN_SELECTOR, liquidityAmount); + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + uint256 amount = 1e6; + + vm.startPrank(s_routerAllowedOffRamp); + + vm.expectEmit(); + emit TokenPool.Released(s_routerAllowedOffRamp, recipient, amount); + + Pool.ReleaseOrMintOutV1 memory poolReturnDataV1 = s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), + offchainTokenData: "" + }) + ); + + assertEq(poolReturnDataV1.destinationAmount, amount, "destinationAmount and actual amount transferred differ"); + + // Simulate the off-ramp forwarding tokens to the recipient on destination chain + // s_token.transfer(recipient, amount); + + assertEq( + s_token.balanceOf(address(s_usdcTokenPool)), + liquidityAmount - amount, + "Incorrect remaining liquidity in TokenPool" + ); + assertEq(s_token.balanceOf(recipient), amount, "Tokens not transferred to recipient"); + } + + // https://etherscan.io/tx/0xac9f501fe0b76df1f07a22e1db30929fd12524bc7068d74012dff948632f0883 + function test_incomingMessageWithPrimaryMechanism() public { + bytes memory encodedUsdcMessage = + hex"000000000000000300000000000000000000127a00000000000000000000000019330d10d9cc8751218eaf51e8885d058642e08a000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e58310000000000000000000000004af08f56978be7dce2d1be3c65c005b41e79401c000000000000000000000000000000000000000000000000000000002057ff7a0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000000000000000000000000000000000000000000000000000000000008274119237535fd659626b090f87e365ff89ebc7096bb32e8b0e85f155626b73ae7c4bb2485c184b7cc3cf7909045487890b104efb62ae74a73e32901bdcec91df1bb9ee08ccb014fcbcfe77b74d1263fd4e0b0e8de05d6c9a5913554364abfd5ea768b222f50c715908183905d74044bb2b97527c7e70ae7983c443a603557cac3b1c000000000000000000000000000000000000000000000000000000000000"; + bytes memory attestation = bytes("attestation bytes"); + + uint32 nonce = 4730; + uint32 sourceDomain = 3; + uint256 amount = 100; + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: nonce, sourceDomain: sourceDomain})), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + // The mocked receiver does not release the token to the pool, so we manually do it here + deal(address(s_token), address(s_usdcTokenPool), amount); + + bytes memory offchainTokenData = + abi.encode(USDCTokenPool.MessageAndAttestation({message: encodedUsdcMessage, attestation: attestation})); + + vm.expectCall( + address(s_mockUSDCTransmitter), + abi.encodeWithSelector(MockE2EUSDCTransmitter.receiveMessage.selector, encodedUsdcMessage, attestation) + ); + + vm.startPrank(s_routerAllowedOffRamp); + s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: OWNER, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: sourceTokenData.extraData, + offchainTokenData: offchainTokenData + }) + ); + } + + function test_WhileMigrationPause_Revert() public { + address recipient = address(1234); + + // Designate the SOURCE_CHAIN as not using native-USDC, and so the L/R mechanism must be used instead + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = SOURCE_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + assertTrue( + s_usdcTokenPool.shouldUseLockRelease(SOURCE_CHAIN_SELECTOR), + "Lock/Release mech not configured for incoming message from SOURCE_CHAIN_SELECTOR" + ); + + vm.startPrank(OWNER); + + vm.expectEmit(); + emit USDCBridgeMigrator.CCTPMigrationProposed(SOURCE_CHAIN_SELECTOR); + + // Propose the migration to CCTP + s_usdcTokenPool.proposeCCTPMigration(SOURCE_CHAIN_SELECTOR); + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + bytes memory sourcePoolDataLockRelease = abi.encode(LOCK_RELEASE_FLAG); + + uint256 amount = 1e6; + + vm.startPrank(s_routerAllowedOffRamp); + + // Expect revert because the lane is paused and no incoming messages should be allowed + vm.expectRevert( + abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, SOURCE_CHAIN_SELECTOR) + ); + + s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: sourcePoolDataLockRelease, + offchainTokenData: "" + }) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.transferLiquidity.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.transferLiquidity.t.sol new file mode 100644 index 00000000000..07eeadf486a --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.transferLiquidity.t.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {ILiquidityContainer} from "../../../../../liquiditymanager/interfaces/ILiquidityContainer.sol"; +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract HybridLockReleaseUSDCTokenPool_TransferLiquidity is USDCTokenPoolSetup { + function test_transferLiquidity_Success() public { + // Set as the OWNER so we can provide liquidity + vm.startPrank(OWNER); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_token.approve(address(s_usdcTokenPool), type(uint256).max); + + uint256 liquidityAmount = 1e9; + + // Provide some liquidity to the pool + s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, liquidityAmount); + + // Set the new token pool as the rebalancer + s_usdcTokenPool.transferOwnership(address(s_usdcTokenPoolTransferLiquidity)); + + vm.expectEmit(); + emit ILiquidityContainer.LiquidityRemoved(address(s_usdcTokenPoolTransferLiquidity), liquidityAmount); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityTransferred( + address(s_usdcTokenPool), DEST_CHAIN_SELECTOR, liquidityAmount + ); + + s_usdcTokenPoolTransferLiquidity.transferLiquidity(address(s_usdcTokenPool), DEST_CHAIN_SELECTOR); + + assertEq( + s_usdcTokenPool.owner(), + address(s_usdcTokenPoolTransferLiquidity), + "Ownership of the old pool should be transferred to the new pool" + ); + + assertEq( + s_usdcTokenPoolTransferLiquidity.getLockedTokensForChain(DEST_CHAIN_SELECTOR), + liquidityAmount, + "Tokens locked for dest chain doesn't match expected amount in storage" + ); + + assertEq( + s_usdcTokenPool.getLockedTokensForChain(DEST_CHAIN_SELECTOR), + 0, + "Tokens locked for dest chain in old token pool doesn't match expected amount in storage" + ); + + assertEq( + s_token.balanceOf(address(s_usdcTokenPoolTransferLiquidity)), + liquidityAmount, + "Liquidity amount of tokens should be new in new pool, but aren't" + ); + + assertEq( + s_token.balanceOf(address(s_usdcTokenPool)), + 0, + "Liquidity amount of tokens should be zero in old pool, but aren't" + ); + } + + function test_cannotTransferLiquidityDuringPendingMigration_Revert() public { + // Set as the OWNER so we can provide liquidity + vm.startPrank(OWNER); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_token.approve(address(s_usdcTokenPool), type(uint256).max); + + uint256 liquidityAmount = 1e9; + + // Provide some liquidity to the pool + s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, liquidityAmount); + + // Set the new token pool as the rebalancer + s_usdcTokenPool.transferOwnership(address(s_usdcTokenPoolTransferLiquidity)); + + s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); + + vm.expectRevert( + abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, DEST_CHAIN_SELECTOR) + ); + + s_usdcTokenPoolTransferLiquidity.transferLiquidity(address(s_usdcTokenPool), DEST_CHAIN_SELECTOR); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.burnLockedUSDC.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.burnLockedUSDC.t.sol new file mode 100644 index 00000000000..b95d821bb88 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.burnLockedUSDC.t.sol @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; +import {Pool} from "../../../../libraries/Pool.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCBridgeMigrator} from "../../../../pools/USDC/USDCBridgeMigrator.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; +import {HybridLockReleaseUSDCTokenPool_lockOrBurn} from + "../HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.lockOrBurn.t.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract USDCBridgeMigrator_BurnLockedUSDC is HybridLockReleaseUSDCTokenPool_lockOrBurn { + function test_lockOrBurn_then_BurnInCCTPMigration_Success() public { + bytes32 receiver = bytes32(uint256(uint160(STRANGER))); + address CIRCLE = makeAddr("CIRCLE CCTP Migrator"); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + assertTrue( + s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), + "Lock/Release mech not configured for outgoing message to DEST_CHAIN_SELECTOR" + ); + + uint256 amount = 1e6; + + s_token.transfer(address(s_usdcTokenPool), amount); + + vm.startPrank(s_routerAllowedOnRamp); + + vm.expectEmit(); + emit TokenPool.Locked(s_routerAllowedOnRamp, amount); + + s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(receiver), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + + // Ensure that the tokens are properly locked + assertEq(s_token.balanceOf(address(s_usdcTokenPool)), amount, "Incorrect token amount in the tokenPool"); + + assertEq( + s_usdcTokenPool.getLockedTokensForChain(DEST_CHAIN_SELECTOR), + amount, + "Internal locked token accounting is incorrect" + ); + + vm.startPrank(OWNER); + + vm.expectEmit(); + emit USDCBridgeMigrator.CircleMigratorAddressSet(CIRCLE); + + s_usdcTokenPool.setCircleMigratorAddress(CIRCLE); + + vm.expectEmit(); + emit USDCBridgeMigrator.CCTPMigrationProposed(DEST_CHAIN_SELECTOR); + + // Propose the migration to CCTP + s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); + + assertEq( + s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), + DEST_CHAIN_SELECTOR, + "Current proposed chain migration does not match expected for DEST_CHAIN_SELECTOR" + ); + + // Impersonate the set circle address and execute the proposal + vm.startPrank(CIRCLE); + + vm.expectEmit(); + emit USDCBridgeMigrator.CCTPMigrationExecuted(DEST_CHAIN_SELECTOR, amount); + + // Ensure the call to the burn function is properly + vm.expectCall(address(s_token), abi.encodeWithSelector(bytes4(keccak256("burn(uint256)")), amount)); + + s_usdcTokenPool.burnLockedUSDC(); + + // Assert that the tokens were actually burned + assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 0, "Tokens were not burned out of the tokenPool"); + + // Ensure the proposal slot was cleared and there's no tokens locked for the destination chain anymore + assertEq(s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), 0, "Proposal Slot should be empty"); + assertEq( + s_usdcTokenPool.getLockedTokensForChain(DEST_CHAIN_SELECTOR), + 0, + "No tokens should be locked for DEST_CHAIN_SELECTOR after CCTP-approved burn" + ); + + assertFalse( + s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), "Lock/Release mech should be disabled after a burn" + ); + + test_PrimaryMechanism_Success(); + } + + function test_invalidPermissions_Revert() public { + address CIRCLE = makeAddr("CIRCLE"); + + vm.startPrank(OWNER); + + // Set the circle migrator address for later, but don't start pranking as it yet + s_usdcTokenPool.setCircleMigratorAddress(CIRCLE); + + vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.onlyCircle.selector)); + + // Should fail because only Circle can call this function + s_usdcTokenPool.burnLockedUSDC(); + + vm.startPrank(CIRCLE); + + vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.NoMigrationProposalPending.selector)); + s_usdcTokenPool.burnLockedUSDC(); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.cancelMigrationProposal.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.cancelMigrationProposal.t.sol new file mode 100644 index 00000000000..513361f096c --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.cancelMigrationProposal.t.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCBridgeMigrator} from "../../../../pools/USDC/USDCBridgeMigrator.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract USDCBridgeMigrator_cancelMigrationProposal is USDCTokenPoolSetup { + function test_cancelExistingCCTPMigrationProposal_Success() public { + vm.startPrank(OWNER); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + vm.expectEmit(); + emit USDCBridgeMigrator.CCTPMigrationProposed(DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); + + assertEq( + s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), + DEST_CHAIN_SELECTOR, + "migration proposal should exist, but doesn't" + ); + + vm.expectEmit(); + emit USDCBridgeMigrator.CCTPMigrationCancelled(DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); + + assertEq( + s_usdcTokenPool.getCurrentProposedCCTPChainMigration(), + 0, + "migration proposal exists, but shouldn't after being cancelled" + ); + + vm.expectRevert(USDCBridgeMigrator.NoMigrationProposalPending.selector); + s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); + } + + function test_cannotCancelANonExistentMigrationProposal_Revert() public { + vm.expectRevert(USDCBridgeMigrator.NoMigrationProposalPending.selector); + + // Proposal to migrate doesn't exist, and so the chain selector is zero, and therefore should revert + s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.excludeTokensFromBurn.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.excludeTokensFromBurn.t.sol new file mode 100644 index 00000000000..11cffd0e03d --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.excludeTokensFromBurn.t.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCBridgeMigrator} from "../../../../pools/USDC/USDCBridgeMigrator.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract USDCBridgeMigrator_excludeTokensFromBurn is USDCTokenPoolSetup { + function test_excludeTokensWhenNoMigrationProposalPending_Revert() public { + vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.NoMigrationProposalPending.selector)); + + vm.startPrank(OWNER); + + s_usdcTokenPool.excludeTokensFromBurn(SOURCE_CHAIN_SELECTOR, 1e6); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.proposeMigration.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.proposeMigration.t.sol new file mode 100644 index 00000000000..d445cbac896 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.proposeMigration.t.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCBridgeMigrator} from "../../../../pools/USDC/USDCBridgeMigrator.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract USDCBridgeMigrator_proposeMigration is USDCTokenPoolSetup { + function test_ChainNotUsingLockRelease_Revert() public { + vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.InvalidChainSelector.selector)); + + vm.startPrank(OWNER); + + s_usdcTokenPool.proposeCCTPMigration(0x98765); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.provideLiquidity.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.provideLiquidity.t.sol new file mode 100644 index 00000000000..a94cd4df348 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.provideLiquidity.t.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; +import {USDCBridgeMigrator_BurnLockedUSDC} from "./USDCBridgeMigrator.burnLockedUSDC.t.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract USDCBridgeMigrator_provideLiquidity is USDCBridgeMigrator_BurnLockedUSDC { + function test_cannotModifyLiquidityWithoutPermissions_Revert() public { + address randomAddr = makeAddr("RANDOM"); + + vm.startPrank(randomAddr); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.Unauthorized.selector, randomAddr)); + + // Revert because there's insufficient permissions for the DEST_CHAIN_SELECTOR to provide liquidity + s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); + } + + function test_cannotProvideLiquidity_AfterMigration_Revert() public { + test_lockOrBurn_then_BurnInCCTPMigration_Success(); + + vm.startPrank(OWNER); + + vm.expectRevert( + abi.encodeWithSelector( + HybridLockReleaseUSDCTokenPool.TokenLockingNotAllowedAfterMigration.selector, DEST_CHAIN_SELECTOR + ) + ); + + s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); + } + + function test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() public { + vm.startPrank(OWNER); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); + + vm.expectRevert( + abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, DEST_CHAIN_SELECTOR) + ); + s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.releaseOrMint.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.releaseOrMint.t.sol new file mode 100644 index 00000000000..9976adf64ea --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.releaseOrMint.t.sol @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; +import {Internal} from "../../../../libraries/Internal.sol"; +import {Pool} from "../../../../libraries/Pool.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {LOCK_RELEASE_FLAG} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCBridgeMigrator} from "../../../../pools/USDC/USDCBridgeMigrator.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; +import {HybridLockReleaseUSDCTokenPool_releaseOrMint} from + "../HybridLockReleaseUSDCTokenPool/HybridLockReleaseUSDCTokenPool.releaseOrMint.t.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract USDCBridgeMigrator_releaseOrMint is HybridLockReleaseUSDCTokenPool_releaseOrMint { + function test_unstickManualTxAfterMigration_destChain_Success() public { + address recipient = address(1234); + // Test the edge case where a tx is stuck in the manual tx queue and the destination chain is the one that + // should process is after a migration. I.E the message will have the Lock-Release flag set in the OffChainData, + // which should tell it to use the lock-release mechanism with the tokens provided. + + // We want the released amount to be 1e6, so to simulate the workflow, we sent those tokens to the contract as + // liquidity + uint256 amount = 1e6; + // Add 1e12 liquidity so that there's enough to release + vm.startPrank(s_usdcTokenPool.getLiquidityProvider(SOURCE_CHAIN_SELECTOR)); + + s_token.approve(address(s_usdcTokenPool), type(uint256).max); + s_usdcTokenPool.provideLiquidity(SOURCE_CHAIN_SELECTOR, amount); + + // By Default, the source chain will be indicated as use-CCTP so we need to change that. We create a message + // that will use the Lock-Release flag in the offchain data to indicate that the tokens should be released + // instead of minted since there's no attestation for us to use. + + vm.startPrank(s_routerAllowedOffRamp); + + vm.expectEmit(); + emit TokenPool.Released(s_routerAllowedOffRamp, recipient, amount); + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + Pool.ReleaseOrMintOutV1 memory poolReturnDataV1 = s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), + offchainTokenData: "" + }) + ); + + // By this point, the tx should have executed, with the Lock-Release taking over, and being forwaded to the + // recipient + + assertEq(poolReturnDataV1.destinationAmount, amount, "destinationAmount and actual amount transferred differ"); + assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 0, "Tokens should be transferred out of the pool"); + assertEq(s_token.balanceOf(recipient), amount, "Tokens should be transferred to the recipient"); + + // We also want to check that the system uses CCTP Burn/Mint for all other messages that don't have that flag + // which after a migration will mean all new messages. + + // The message should fail without an error because it failed to decode a non-existent attestation which would + // revert without an error + vm.expectRevert(); + + s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: "", + offchainTokenData: "" + }) + ); + } + + function test_unstickManualTxAfterMigration_homeChain_Success() public { + address CIRCLE = makeAddr("CIRCLE"); + address recipient = address(1234); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = SOURCE_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + // Test the edge case where a tx is stuck in the manual tx queue and the source chain (mainnet) needs unsticking + // In this test we want 1e6 worth of tokens to be stuck, so first we provide liquidity to the pool >1e6 + + uint256 amount = 1e6; + // Add 1e12 liquidity so that there's enough to release + vm.startPrank(s_usdcTokenPool.getLiquidityProvider(SOURCE_CHAIN_SELECTOR)); + + s_token.approve(address(s_usdcTokenPool), type(uint256).max); + + // I picked 3x the amount to be stuck so that we can have enough to release with a buffer + s_usdcTokenPool.provideLiquidity(SOURCE_CHAIN_SELECTOR, amount * 3); + + // At this point in the process, the router will lock new messages, so we want to simulate excluding tokens + // stuck coming back from the destination, to the home chain. This way they can be released and not minted + // since there's no corresponding attestation to use for minting. + vm.startPrank(OWNER); + + s_usdcTokenPool.proposeCCTPMigration(SOURCE_CHAIN_SELECTOR); + + // Exclude the tokens from being burned and check for the event + vm.expectEmit(); + emit USDCBridgeMigrator.TokensExcludedFromBurn(SOURCE_CHAIN_SELECTOR, amount, (amount * 3) - amount); + + s_usdcTokenPool.excludeTokensFromBurn(SOURCE_CHAIN_SELECTOR, amount); + + assertEq( + s_usdcTokenPool.getLockedTokensForChain(SOURCE_CHAIN_SELECTOR), + (amount * 3), + "Tokens locked minus ones excluded from the burn should be 2e6" + ); + + assertEq( + s_usdcTokenPool.getExcludedTokensByChain(SOURCE_CHAIN_SELECTOR), + 1e6, + "1e6 tokens should be excluded from the burn" + ); + + s_usdcTokenPool.setCircleMigratorAddress(CIRCLE); + + vm.startPrank(CIRCLE); + + s_usdcTokenPool.burnLockedUSDC(); + + assertEq( + s_usdcTokenPool.getLockedTokensForChain(SOURCE_CHAIN_SELECTOR), 0, "All tokens should be burned out of the pool" + ); + + assertEq( + s_usdcTokenPool.getExcludedTokensByChain(SOURCE_CHAIN_SELECTOR), + 1e6, + "There should still be 1e6 tokens excluded from the burn" + ); + + assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 1e6, "All tokens minus the excluded should be in the pool"); + + // Now that the burn is successful, we can release the tokens that were excluded from the burn + vm.startPrank(s_routerAllowedOffRamp); + + vm.expectEmit(); + emit TokenPool.Released(s_routerAllowedOffRamp, recipient, amount); + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + Pool.ReleaseOrMintOutV1 memory poolReturnDataV1 = s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), + offchainTokenData: "" + }) + ); + + assertEq(poolReturnDataV1.destinationAmount, amount, "destinationAmount and actual amount transferred differ"); + assertEq(s_token.balanceOf(address(s_usdcTokenPool)), 0, "Tokens should be transferred out of the pool"); + assertEq(s_token.balanceOf(recipient), amount, "Tokens should be transferred to the recipient"); + assertEq( + s_usdcTokenPool.getExcludedTokensByChain(SOURCE_CHAIN_SELECTOR), + 0, + "All tokens should be released from the exclusion list" + ); + + // We also want to check that the system uses CCTP Burn/Mint for all other messages that don't have that flag + test_incomingMessageWithPrimaryMechanism(); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.updateChainSelectorMechanism.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.updateChainSelectorMechanism.t.sol new file mode 100644 index 00000000000..da3e15bc8ad --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCBridgeMigrator/USDCBridgeMigrator.updateChainSelectorMechanism.t.sol @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; + +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; +import {USDCBridgeMigrator_BurnLockedUSDC} from "./USDCBridgeMigrator.burnLockedUSDC.t.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPool; + HybridLockReleaseUSDCTokenPool internal s_usdcTokenPoolTransferLiquidity; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + s_usdcTokenPoolTransferLiquidity = + new HybridLockReleaseUSDCTokenPool(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + usdcToken.grantMintAndBurnRoles(address(s_usdcTokenPool)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + + vm.expectEmit(); + emit HybridLockReleaseUSDCTokenPool.LiquidityProviderSet(address(0), OWNER, DEST_CHAIN_SELECTOR); + + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); + s_usdcTokenPool.setLiquidityProvider(SOURCE_CHAIN_SELECTOR, OWNER); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} + +contract USDCBridgeMigrator_updateChainSelectorMechanism is USDCBridgeMigrator_BurnLockedUSDC { + function test_cannotRevertChainMechanism_afterMigration_Revert() public { + test_lockOrBurn_then_BurnInCCTPMigration_Success(); + + vm.startPrank(OWNER); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + vm.expectRevert( + abi.encodeWithSelector( + HybridLockReleaseUSDCTokenPool.TokenLockingNotAllowedAfterMigration.selector, DEST_CHAIN_SELECTOR + ) + ); + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.lockOrBurn.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.lockOrBurn.t.sol new file mode 100644 index 00000000000..2ca33ad4f5f --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.lockOrBurn.t.sol @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {ITokenMessenger} from "../../../../pools/USDC/ITokenMessenger.sol"; + +import {Router} from "../../../../Router.sol"; +import {Pool} from "../../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {USDCTokenPoolSetup} from "./USDCTokenPoolSetup.t.sol"; + +contract USDCTokenPool_lockOrBurn is USDCTokenPoolSetup { + // Base test case, included for PR gas comparisons as fuzz tests are excluded from forge snapshot due to being flaky. + function test_LockOrBurn_Success() public { + bytes32 receiver = bytes32(uint256(uint160(STRANGER))); + uint256 amount = 1; + s_token.transfer(address(s_usdcTokenPool), amount); + vm.startPrank(s_routerAllowedOnRamp); + + USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPool.getDomain(DEST_CHAIN_SELECTOR); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + + vm.expectEmit(); + emit ITokenMessenger.DepositForBurn( + s_mockUSDC.s_nonce(), + address(s_token), + amount, + address(s_usdcTokenPool), + receiver, + expectedDomain.domainIdentifier, + s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), + expectedDomain.allowedCaller + ); + + vm.expectEmit(); + emit TokenPool.Burned(s_routerAllowedOnRamp, amount); + + Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(receiver), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + + uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); + assertEq(s_mockUSDC.s_nonce() - 1, nonce); + } + + function test_Fuzz_LockOrBurn_Success(bytes32 destinationReceiver, uint256 amount) public { + vm.assume(destinationReceiver != bytes32(0)); + amount = bound(amount, 1, _getOutboundRateLimiterConfig().capacity); + s_token.transfer(address(s_usdcTokenPool), amount); + vm.startPrank(s_routerAllowedOnRamp); + + USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPool.getDomain(DEST_CHAIN_SELECTOR); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + + vm.expectEmit(); + emit ITokenMessenger.DepositForBurn( + s_mockUSDC.s_nonce(), + address(s_token), + amount, + address(s_usdcTokenPool), + destinationReceiver, + expectedDomain.domainIdentifier, + s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), + expectedDomain.allowedCaller + ); + + vm.expectEmit(); + emit TokenPool.Burned(s_routerAllowedOnRamp, amount); + + Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(destinationReceiver), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + + uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); + assertEq(s_mockUSDC.s_nonce() - 1, nonce); + assertEq(poolReturnDataV1.destTokenAddress, abi.encode(DEST_CHAIN_USDC_TOKEN)); + } + + function test_Fuzz_LockOrBurnWithAllowList_Success(bytes32 destinationReceiver, uint256 amount) public { + vm.assume(destinationReceiver != bytes32(0)); + amount = bound(amount, 1, _getOutboundRateLimiterConfig().capacity); + s_token.transfer(address(s_usdcTokenPoolWithAllowList), amount); + vm.startPrank(s_routerAllowedOnRamp); + + USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPoolWithAllowList.getDomain(DEST_CHAIN_SELECTOR); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(amount); + vm.expectEmit(); + emit ITokenMessenger.DepositForBurn( + s_mockUSDC.s_nonce(), + address(s_token), + amount, + address(s_usdcTokenPoolWithAllowList), + destinationReceiver, + expectedDomain.domainIdentifier, + s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), + expectedDomain.allowedCaller + ); + vm.expectEmit(); + emit TokenPool.Burned(s_routerAllowedOnRamp, amount); + + Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPoolWithAllowList.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: s_allowedList[0], + receiver: abi.encodePacked(destinationReceiver), + amount: amount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); + assertEq(s_mockUSDC.s_nonce() - 1, nonce); + assertEq(poolReturnDataV1.destTokenAddress, abi.encode(DEST_CHAIN_USDC_TOKEN)); + } + + // Reverts + function test_UnknownDomain_Revert() public { + uint64 wrongDomain = DEST_CHAIN_SELECTOR + 1; + // We need to setup the wrong chainSelector so it reaches the domain check + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: wrongDomain, onRamp: s_routerAllowedOnRamp}); + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: wrongDomain, + remotePoolAddress: abi.encode(address(1)), + remoteTokenAddress: abi.encode(address(2)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + + uint256 amount = 1000; + vm.startPrank(s_routerAllowedOnRamp); + deal(address(s_token), s_routerAllowedOnRamp, amount); + s_token.approve(address(s_usdcTokenPool), amount); + + vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.UnknownDomain.selector, wrongDomain)); + + s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(address(0)), + amount: amount, + remoteChainSelector: wrongDomain, + localToken: address(s_token) + }) + ); + } + + function test_CallerIsNotARampOnRouter_Revert() public { + vm.expectRevert(abi.encodeWithSelector(TokenPool.CallerIsNotARampOnRouter.selector, OWNER)); + + s_usdcTokenPool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: abi.encodePacked(address(0)), + amount: 0, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + } + + function test_LockOrBurnWithAllowList_Revert() public { + vm.startPrank(s_routerAllowedOnRamp); + + vm.expectRevert(abi.encodeWithSelector(TokenPool.SenderNotAllowed.selector, STRANGER)); + + s_usdcTokenPoolWithAllowList.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: STRANGER, + receiver: abi.encodePacked(address(0)), + amount: 1000, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_token) + }) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.releaseOrMint.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.releaseOrMint.t.sol new file mode 100644 index 00000000000..f4ffde6c82c --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.releaseOrMint.t.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Internal} from "../../../../libraries/Internal.sol"; +import {Pool} from "../../../../libraries/Pool.sol"; +import {RateLimiter} from "../../../../libraries/RateLimiter.sol"; +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {USDCTokenPoolSetup} from "./USDCTokenPoolSetup.t.sol"; + +contract USDCTokenPool_releaseOrMint is USDCTokenPoolSetup { + // From https://github.com/circlefin/evm-cctp-contracts/blob/377c9bd813fb86a42d900ae4003599d82aef635a/src/messages/BurnMessage.sol#L57 + function _formatMessage( + uint32 _version, + bytes32 _burnToken, + bytes32 _mintRecipient, + uint256 _amount, + bytes32 _messageSender + ) internal pure returns (bytes memory) { + return abi.encodePacked(_version, _burnToken, _mintRecipient, _amount, _messageSender); + } + + function test_Fuzz_ReleaseOrMint_Success(address recipient, uint256 amount) public { + vm.assume(recipient != address(0) && recipient != address(s_token)); + amount = bound(amount, 0, _getInboundRateLimiterConfig().capacity); + + USDCMessage memory usdcMessage = USDCMessage({ + version: 0, + sourceDomain: SOURCE_DOMAIN_IDENTIFIER, + destinationDomain: DEST_DOMAIN_IDENTIFIER, + nonce: 0x060606060606, + sender: SOURCE_CHAIN_TOKEN_SENDER, + recipient: bytes32(uint256(uint160(recipient))), + destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), + messageBody: _formatMessage( + 0, + bytes32(uint256(uint160(address(s_token)))), + bytes32(uint256(uint160(recipient))), + amount, + bytes32(uint256(uint160(OWNER))) + ) + }); + + bytes memory message = _generateUSDCMessage(usdcMessage); + bytes memory attestation = bytes("attestation bytes"); + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode( + USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: SOURCE_DOMAIN_IDENTIFIER}) + ), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + bytes memory offchainTokenData = + abi.encode(USDCTokenPool.MessageAndAttestation({message: message, attestation: attestation})); + + // The mocked receiver does not release the token to the pool, so we manually do it here + deal(address(s_token), address(s_usdcTokenPool), amount); + + vm.expectEmit(); + emit TokenPool.Minted(s_routerAllowedOffRamp, recipient, amount); + + vm.expectCall( + address(s_mockUSDCTransmitter), + abi.encodeWithSelector(MockE2EUSDCTransmitter.receiveMessage.selector, message, attestation) + ); + + vm.startPrank(s_routerAllowedOffRamp); + s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: sourceTokenData.extraData, + offchainTokenData: offchainTokenData + }) + ); + } + + // https://etherscan.io/tx/0xac9f501fe0b76df1f07a22e1db30929fd12524bc7068d74012dff948632f0883 + function test_ReleaseOrMintRealTx_Success() public { + bytes memory encodedUsdcMessage = + hex"000000000000000300000000000000000000127a00000000000000000000000019330d10d9cc8751218eaf51e8885d058642e08a000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e58310000000000000000000000004af08f56978be7dce2d1be3c65c005b41e79401c000000000000000000000000000000000000000000000000000000002057ff7a0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000000000000000000000000000000000000000000000000000000000008274119237535fd659626b090f87e365ff89ebc7096bb32e8b0e85f155626b73ae7c4bb2485c184b7cc3cf7909045487890b104efb62ae74a73e32901bdcec91df1bb9ee08ccb014fcbcfe77b74d1263fd4e0b0e8de05d6c9a5913554364abfd5ea768b222f50c715908183905d74044bb2b97527c7e70ae7983c443a603557cac3b1c000000000000000000000000000000000000000000000000000000000000"; + bytes memory attestation = bytes("attestation bytes"); + + uint32 nonce = 4730; + uint32 sourceDomain = 3; + uint256 amount = 100; + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: nonce, sourceDomain: sourceDomain})), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + // The mocked receiver does not release the token to the pool, so we manually do it here + deal(address(s_token), address(s_usdcTokenPool), amount); + + bytes memory offchainTokenData = + abi.encode(USDCTokenPool.MessageAndAttestation({message: encodedUsdcMessage, attestation: attestation})); + + vm.expectCall( + address(s_mockUSDCTransmitter), + abi.encodeWithSelector(MockE2EUSDCTransmitter.receiveMessage.selector, encodedUsdcMessage, attestation) + ); + + vm.startPrank(s_routerAllowedOffRamp); + s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: OWNER, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: sourceTokenData.extraData, + offchainTokenData: offchainTokenData + }) + ); + } + + // Reverts + function test_UnlockingUSDCFailed_Revert() public { + vm.startPrank(s_routerAllowedOffRamp); + s_mockUSDCTransmitter.setShouldSucceed(false); + + uint256 amount = 13255235235; + + USDCMessage memory usdcMessage = USDCMessage({ + version: 0, + sourceDomain: SOURCE_DOMAIN_IDENTIFIER, + destinationDomain: DEST_DOMAIN_IDENTIFIER, + nonce: 0x060606060606, + sender: SOURCE_CHAIN_TOKEN_SENDER, + recipient: bytes32(uint256(uint160(address(s_mockUSDC)))), + destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), + messageBody: _formatMessage( + 0, + bytes32(uint256(uint160(address(s_token)))), + bytes32(uint256(uint160(OWNER))), + amount, + bytes32(uint256(uint160(OWNER))) + ) + }); + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode( + USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: SOURCE_DOMAIN_IDENTIFIER}) + ), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + bytes memory offchainTokenData = abi.encode( + USDCTokenPool.MessageAndAttestation({message: _generateUSDCMessage(usdcMessage), attestation: bytes("")}) + ); + + vm.expectRevert(USDCTokenPool.UnlockingUSDCFailed.selector); + + s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: OWNER, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: sourceTokenData.extraData, + offchainTokenData: offchainTokenData + }) + ); + } + + function test_TokenMaxCapacityExceeded_Revert() public { + uint256 capacity = _getInboundRateLimiterConfig().capacity; + uint256 amount = 10 * capacity; + address recipient = address(1); + vm.startPrank(s_routerAllowedOffRamp); + + Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ + sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + destTokenAddress: abi.encode(address(s_usdcTokenPool)), + extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), + destGasAmount: USDC_DEST_TOKEN_GAS + }); + + bytes memory offchainTokenData = + abi.encode(USDCTokenPool.MessageAndAttestation({message: bytes(""), attestation: bytes("")})); + + vm.expectRevert( + abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_token)) + ); + + s_usdcTokenPool.releaseOrMint( + Pool.ReleaseOrMintInV1({ + originalSender: abi.encode(OWNER), + receiver: recipient, + amount: amount, + localToken: address(s_token), + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + sourcePoolAddress: sourceTokenData.sourcePoolAddress, + sourcePoolData: sourceTokenData.extraData, + offchainTokenData: offchainTokenData + }) + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.setDomains.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.setDomains.t.sol new file mode 100644 index 00000000000..1fe5d828bdb --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.setDomains.t.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Ownable2Step} from "../../../../../shared/access/Ownable2Step.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {USDCTokenPoolSetup} from "./USDCTokenPoolSetup.t.sol"; + +contract USDCTokenPool_setDomains is USDCTokenPoolSetup { + mapping(uint64 destChainSelector => USDCTokenPool.Domain domain) private s_chainToDomain; + + // Setting lower fuzz run as 256 runs was causing differing gas results in snapshot. + /// forge-config: default.fuzz.runs = 32 + /// forge-config: ccip.fuzz.runs = 32 + function test_Fuzz_SetDomains_Success( + bytes32[5] calldata allowedCallers, + uint32[5] calldata domainIdentifiers, + uint64[5] calldata destChainSelectors + ) public { + uint256 numberOfDomains = allowedCallers.length; + USDCTokenPool.DomainUpdate[] memory domainUpdates = new USDCTokenPool.DomainUpdate[](numberOfDomains); + for (uint256 i = 0; i < numberOfDomains; ++i) { + vm.assume(allowedCallers[i] != bytes32(0) && domainIdentifiers[i] != 0 && destChainSelectors[i] != 0); + + domainUpdates[i] = USDCTokenPool.DomainUpdate({ + allowedCaller: allowedCallers[i], + domainIdentifier: domainIdentifiers[i], + destChainSelector: destChainSelectors[i], + enabled: true + }); + + s_chainToDomain[destChainSelectors[i]] = + USDCTokenPool.Domain({domainIdentifier: domainIdentifiers[i], allowedCaller: allowedCallers[i], enabled: true}); + } + + vm.expectEmit(); + emit USDCTokenPool.DomainsSet(domainUpdates); + + s_usdcTokenPool.setDomains(domainUpdates); + + for (uint256 i = 0; i < numberOfDomains; ++i) { + USDCTokenPool.Domain memory expected = s_chainToDomain[destChainSelectors[i]]; + USDCTokenPool.Domain memory got = s_usdcTokenPool.getDomain(destChainSelectors[i]); + assertEq(got.allowedCaller, expected.allowedCaller); + assertEq(got.domainIdentifier, expected.domainIdentifier); + } + } + + // Reverts + + function test_OnlyOwner_Revert() public { + USDCTokenPool.DomainUpdate[] memory domainUpdates = new USDCTokenPool.DomainUpdate[](0); + + vm.startPrank(STRANGER); + vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); + + s_usdcTokenPool.setDomains(domainUpdates); + } + + function test_InvalidDomain_Revert() public { + bytes32 validCaller = bytes32(uint256(25)); + // Ensure valid domain works + USDCTokenPool.DomainUpdate[] memory domainUpdates = new USDCTokenPool.DomainUpdate[](1); + domainUpdates[0] = USDCTokenPool.DomainUpdate({ + allowedCaller: validCaller, + domainIdentifier: 0, // ensures 0 is valid, as this is eth mainnet + destChainSelector: 45690, + enabled: true + }); + + s_usdcTokenPool.setDomains(domainUpdates); + + // Make update invalid on allowedCaller + domainUpdates[0].allowedCaller = bytes32(0); + vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidDomain.selector, domainUpdates[0])); + + s_usdcTokenPool.setDomains(domainUpdates); + + // Make valid again + domainUpdates[0].allowedCaller = validCaller; + + // Make invalid on destChainSelector + domainUpdates[0].destChainSelector = 0; + vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidDomain.selector, domainUpdates[0])); + + s_usdcTokenPool.setDomains(domainUpdates); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.supportsInterface.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.supportsInterface.t.sol new file mode 100644 index 00000000000..05ac5f08136 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.supportsInterface.t.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IPoolV1} from "../../../../interfaces/IPool.sol"; +import {USDCTokenPoolSetup} from "./USDCTokenPoolSetup.t.sol"; + +import {IERC165} from "../../../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol"; + +contract USDCTokenPool_supportsInterface is USDCTokenPoolSetup { + function test_SupportsInterface_Success() public view { + assertTrue(s_usdcTokenPool.supportsInterface(type(IPoolV1).interfaceId)); + assertTrue(s_usdcTokenPool.supportsInterface(type(IERC165).interfaceId)); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.validateMessage.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.validateMessage.t.sol new file mode 100644 index 00000000000..c53fa6e81b9 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPool.validateMessage.t.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {USDCTokenPoolSetup} from "./USDCTokenPoolSetup.t.sol"; + +contract USDCTokenPool__validateMessage is USDCTokenPoolSetup { + function test_Fuzz_ValidateMessage_Success(uint32 sourceDomain, uint64 nonce) public { + vm.pauseGasMetering(); + USDCMessage memory usdcMessage = USDCMessage({ + version: 0, + sourceDomain: sourceDomain, + destinationDomain: DEST_DOMAIN_IDENTIFIER, + nonce: nonce, + sender: SOURCE_CHAIN_TOKEN_SENDER, + recipient: bytes32(uint256(299999)), + destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), + messageBody: bytes("") + }); + + bytes memory encodedUsdcMessage = _generateUSDCMessage(usdcMessage); + + vm.resumeGasMetering(); + s_usdcTokenPool.validateMessage( + encodedUsdcMessage, USDCTokenPool.SourceTokenDataPayload({nonce: nonce, sourceDomain: sourceDomain}) + ); + } + + // Reverts + + function test_ValidateInvalidMessage_Revert() public { + USDCMessage memory usdcMessage = USDCMessage({ + version: 0, + sourceDomain: 1553252, + destinationDomain: DEST_DOMAIN_IDENTIFIER, + nonce: 387289284924, + sender: SOURCE_CHAIN_TOKEN_SENDER, + recipient: bytes32(uint256(92398429395823)), + destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), + messageBody: bytes("") + }); + + USDCTokenPool.SourceTokenDataPayload memory sourceTokenData = + USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: usdcMessage.sourceDomain}); + + bytes memory encodedUsdcMessage = _generateUSDCMessage(usdcMessage); + + s_usdcTokenPool.validateMessage(encodedUsdcMessage, sourceTokenData); + + uint32 expectedSourceDomain = usdcMessage.sourceDomain + 1; + + vm.expectRevert( + abi.encodeWithSelector(USDCTokenPool.InvalidSourceDomain.selector, expectedSourceDomain, usdcMessage.sourceDomain) + ); + s_usdcTokenPool.validateMessage( + encodedUsdcMessage, + USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: expectedSourceDomain}) + ); + + uint64 expectedNonce = usdcMessage.nonce + 1; + + vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidNonce.selector, expectedNonce, usdcMessage.nonce)); + s_usdcTokenPool.validateMessage( + encodedUsdcMessage, + USDCTokenPool.SourceTokenDataPayload({nonce: expectedNonce, sourceDomain: usdcMessage.sourceDomain}) + ); + + usdcMessage.destinationDomain = DEST_DOMAIN_IDENTIFIER + 1; + vm.expectRevert( + abi.encodeWithSelector( + USDCTokenPool.InvalidDestinationDomain.selector, DEST_DOMAIN_IDENTIFIER, usdcMessage.destinationDomain + ) + ); + + s_usdcTokenPool.validateMessage( + _generateUSDCMessage(usdcMessage), + USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: usdcMessage.sourceDomain}) + ); + usdcMessage.destinationDomain = DEST_DOMAIN_IDENTIFIER; + + uint32 wrongVersion = usdcMessage.version + 1; + + usdcMessage.version = wrongVersion; + encodedUsdcMessage = _generateUSDCMessage(usdcMessage); + + vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidMessageVersion.selector, wrongVersion)); + s_usdcTokenPool.validateMessage(encodedUsdcMessage, sourceTokenData); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPoolSetup.t.sol b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPoolSetup.t.sol new file mode 100644 index 00000000000..614da422bb4 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/USDC/USDCTokenPool/USDCTokenPoolSetup.t.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {IBurnMintERC20} from "../../../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {BurnMintERC677} from "../../../../../shared/token/ERC677/BurnMintERC677.sol"; +import {Router} from "../../../../Router.sol"; +import {TokenPool} from "../../../../pools/TokenPool.sol"; +import {USDCTokenPool} from "../../../../pools/USDC/USDCTokenPool.sol"; +import {BaseTest} from "../../../BaseTest.t.sol"; +import {USDCTokenPoolHelper} from "../../../helpers/USDCTokenPoolHelper.sol"; +import {MockE2EUSDCTransmitter} from "../../../mocks/MockE2EUSDCTransmitter.sol"; +import {MockUSDCTokenMessenger} from "../../../mocks/MockUSDCTokenMessenger.sol"; + +contract USDCTokenPoolSetup is BaseTest { + IBurnMintERC20 internal s_token; + MockUSDCTokenMessenger internal s_mockUSDC; + MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; + uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; + + struct USDCMessage { + uint32 version; + uint32 sourceDomain; + uint32 destinationDomain; + uint64 nonce; + bytes32 sender; + bytes32 recipient; + bytes32 destinationCaller; + bytes messageBody; + } + + uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; + uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; + + bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); + address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); + address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); + address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); + + address internal s_routerAllowedOnRamp = address(3456); + address internal s_routerAllowedOffRamp = address(234); + Router internal s_router; + + USDCTokenPoolHelper internal s_usdcTokenPool; + USDCTokenPoolHelper internal s_usdcTokenPoolWithAllowList; + address[] internal s_allowedList; + + function setUp() public virtual override { + BaseTest.setUp(); + BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); + s_token = usdcToken; + deal(address(s_token), OWNER, type(uint256).max); + _setUpRamps(); + + s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); + s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); + + usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); + + s_usdcTokenPool = + new USDCTokenPoolHelper(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); + usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); + + s_allowedList.push(USER_1); + s_usdcTokenPoolWithAllowList = + new USDCTokenPoolHelper(s_mockUSDC, s_token, s_allowedList, address(s_mockRMN), address(s_router)); + + TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); + chainUpdates[0] = TokenPool.ChainUpdate({ + remoteChainSelector: SOURCE_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(address(s_token)), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + chainUpdates[1] = TokenPool.ChainUpdate({ + remoteChainSelector: DEST_CHAIN_SELECTOR, + remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), + remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), + allowed: true, + outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), + inboundRateLimiterConfig: _getInboundRateLimiterConfig() + }); + + s_usdcTokenPool.applyChainUpdates(chainUpdates); + s_usdcTokenPoolWithAllowList.applyChainUpdates(chainUpdates); + + USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); + domains[0] = USDCTokenPool.DomainUpdate({ + destChainSelector: DEST_CHAIN_SELECTOR, + domainIdentifier: 9999, + allowedCaller: keccak256("allowedCaller"), + enabled: true + }); + + s_usdcTokenPool.setDomains(domains); + s_usdcTokenPoolWithAllowList.setDomains(domains); + } + + function _setUpRamps() internal { + s_router = new Router(address(s_token), address(s_mockRMN)); + + Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); + onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); + Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); + address[] memory offRamps = new address[](1); + offRamps[0] = s_routerAllowedOffRamp; + offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); + + s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); + } + + function _generateUSDCMessage( + USDCMessage memory usdcMessage + ) internal pure returns (bytes memory) { + return abi.encodePacked( + usdcMessage.version, + usdcMessage.sourceDomain, + usdcMessage.destinationDomain, + usdcMessage.nonce, + usdcMessage.sender, + usdcMessage.recipient, + usdcMessage.destinationCaller, + usdcMessage.messageBody + ); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/USDCTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/USDCTokenPool.t.sol deleted file mode 100644 index d00ef9b8536..00000000000 --- a/contracts/src/v0.8/ccip/test/pools/USDCTokenPool.t.sol +++ /dev/null @@ -1,703 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.24; - -import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; -import {IPoolV1} from "../../interfaces/IPool.sol"; -import {ITokenMessenger} from "../../pools/USDC/ITokenMessenger.sol"; - -import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; -import {BurnMintERC677} from "../../../shared/token/ERC677/BurnMintERC677.sol"; -import {Router} from "../../Router.sol"; -import {Internal} from "../../libraries/Internal.sol"; -import {Pool} from "../../libraries/Pool.sol"; -import {RateLimiter} from "../../libraries/RateLimiter.sol"; -import {TokenPool} from "../../pools/TokenPool.sol"; -import {USDCTokenPool} from "../../pools/USDC/USDCTokenPool.sol"; -import {BaseTest} from "../BaseTest.t.sol"; -import {USDCTokenPoolHelper} from "../helpers/USDCTokenPoolHelper.sol"; -import {MockE2EUSDCTransmitter} from "../mocks/MockE2EUSDCTransmitter.sol"; -import {MockUSDCTokenMessenger} from "../mocks/MockUSDCTokenMessenger.sol"; - -import {IERC165} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol"; - -contract USDCTokenPoolSetup is BaseTest { - IBurnMintERC20 internal s_token; - MockUSDCTokenMessenger internal s_mockUSDC; - MockE2EUSDCTransmitter internal s_mockUSDCTransmitter; - uint32 internal constant USDC_DEST_TOKEN_GAS = 150_000; - - struct USDCMessage { - uint32 version; - uint32 sourceDomain; - uint32 destinationDomain; - uint64 nonce; - bytes32 sender; - bytes32 recipient; - bytes32 destinationCaller; - bytes messageBody; - } - - uint32 internal constant SOURCE_DOMAIN_IDENTIFIER = 0x02020202; - uint32 internal constant DEST_DOMAIN_IDENTIFIER = 0; - - bytes32 internal constant SOURCE_CHAIN_TOKEN_SENDER = bytes32(uint256(uint160(0x01111111221))); - address internal constant SOURCE_CHAIN_USDC_POOL = address(0x23789765456789); - address internal constant DEST_CHAIN_USDC_POOL = address(0x987384873458734); - address internal constant DEST_CHAIN_USDC_TOKEN = address(0x23598918358198766); - - address internal s_routerAllowedOnRamp = address(3456); - address internal s_routerAllowedOffRamp = address(234); - Router internal s_router; - - USDCTokenPoolHelper internal s_usdcTokenPool; - USDCTokenPoolHelper internal s_usdcTokenPoolWithAllowList; - address[] internal s_allowedList; - - function setUp() public virtual override { - BaseTest.setUp(); - BurnMintERC677 usdcToken = new BurnMintERC677("LINK", "LNK", 18, 0); - s_token = usdcToken; - deal(address(s_token), OWNER, type(uint256).max); - _setUpRamps(); - - s_mockUSDCTransmitter = new MockE2EUSDCTransmitter(0, DEST_DOMAIN_IDENTIFIER, address(s_token)); - s_mockUSDC = new MockUSDCTokenMessenger(0, address(s_mockUSDCTransmitter)); - - usdcToken.grantMintAndBurnRoles(address(s_mockUSDCTransmitter)); - - s_usdcTokenPool = - new USDCTokenPoolHelper(s_mockUSDC, s_token, new address[](0), address(s_mockRMN), address(s_router)); - usdcToken.grantMintAndBurnRoles(address(s_mockUSDC)); - - s_allowedList.push(USER_1); - s_usdcTokenPoolWithAllowList = - new USDCTokenPoolHelper(s_mockUSDC, s_token, s_allowedList, address(s_mockRMN), address(s_router)); - - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](2); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - remoteTokenAddress: abi.encode(address(s_token)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - chainUpdates[1] = TokenPool.ChainUpdate({ - remoteChainSelector: DEST_CHAIN_SELECTOR, - remotePoolAddress: abi.encode(DEST_CHAIN_USDC_POOL), - remoteTokenAddress: abi.encode(DEST_CHAIN_USDC_TOKEN), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - - s_usdcTokenPool.applyChainUpdates(chainUpdates); - s_usdcTokenPoolWithAllowList.applyChainUpdates(chainUpdates); - - USDCTokenPool.DomainUpdate[] memory domains = new USDCTokenPool.DomainUpdate[](1); - domains[0] = USDCTokenPool.DomainUpdate({ - destChainSelector: DEST_CHAIN_SELECTOR, - domainIdentifier: 9999, - allowedCaller: keccak256("allowedCaller"), - enabled: true - }); - - s_usdcTokenPool.setDomains(domains); - s_usdcTokenPoolWithAllowList.setDomains(domains); - } - - function _setUpRamps() internal { - s_router = new Router(address(s_token), address(s_mockRMN)); - - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_routerAllowedOnRamp}); - Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1); - address[] memory offRamps = new address[](1); - offRamps[0] = s_routerAllowedOffRamp; - offRampUpdates[0] = Router.OffRamp({sourceChainSelector: SOURCE_CHAIN_SELECTOR, offRamp: offRamps[0]}); - - s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates); - } - - function _generateUSDCMessage( - USDCMessage memory usdcMessage - ) internal pure returns (bytes memory) { - return abi.encodePacked( - usdcMessage.version, - usdcMessage.sourceDomain, - usdcMessage.destinationDomain, - usdcMessage.nonce, - usdcMessage.sender, - usdcMessage.recipient, - usdcMessage.destinationCaller, - usdcMessage.messageBody - ); - } -} - -contract USDCTokenPool_lockOrBurn is USDCTokenPoolSetup { - // Base test case, included for PR gas comparisons as fuzz tests are excluded from forge snapshot due to being flaky. - function test_LockOrBurn_Success() public { - bytes32 receiver = bytes32(uint256(uint160(STRANGER))); - uint256 amount = 1; - s_token.transfer(address(s_usdcTokenPool), amount); - vm.startPrank(s_routerAllowedOnRamp); - - USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPool.getDomain(DEST_CHAIN_SELECTOR); - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - - vm.expectEmit(); - emit ITokenMessenger.DepositForBurn( - s_mockUSDC.s_nonce(), - address(s_token), - amount, - address(s_usdcTokenPool), - receiver, - expectedDomain.domainIdentifier, - s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), - expectedDomain.allowedCaller - ); - - vm.expectEmit(); - emit TokenPool.Burned(s_routerAllowedOnRamp, amount); - - Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(receiver), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - - uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); - assertEq(s_mockUSDC.s_nonce() - 1, nonce); - } - - function test_Fuzz_LockOrBurn_Success(bytes32 destinationReceiver, uint256 amount) public { - vm.assume(destinationReceiver != bytes32(0)); - amount = bound(amount, 1, _getOutboundRateLimiterConfig().capacity); - s_token.transfer(address(s_usdcTokenPool), amount); - vm.startPrank(s_routerAllowedOnRamp); - - USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPool.getDomain(DEST_CHAIN_SELECTOR); - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - - vm.expectEmit(); - emit ITokenMessenger.DepositForBurn( - s_mockUSDC.s_nonce(), - address(s_token), - amount, - address(s_usdcTokenPool), - destinationReceiver, - expectedDomain.domainIdentifier, - s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), - expectedDomain.allowedCaller - ); - - vm.expectEmit(); - emit TokenPool.Burned(s_routerAllowedOnRamp, amount); - - Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(destinationReceiver), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - - uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); - assertEq(s_mockUSDC.s_nonce() - 1, nonce); - assertEq(poolReturnDataV1.destTokenAddress, abi.encode(DEST_CHAIN_USDC_TOKEN)); - } - - function test_Fuzz_LockOrBurnWithAllowList_Success(bytes32 destinationReceiver, uint256 amount) public { - vm.assume(destinationReceiver != bytes32(0)); - amount = bound(amount, 1, _getOutboundRateLimiterConfig().capacity); - s_token.transfer(address(s_usdcTokenPoolWithAllowList), amount); - vm.startPrank(s_routerAllowedOnRamp); - - USDCTokenPool.Domain memory expectedDomain = s_usdcTokenPoolWithAllowList.getDomain(DEST_CHAIN_SELECTOR); - - vm.expectEmit(); - emit RateLimiter.TokensConsumed(amount); - vm.expectEmit(); - emit ITokenMessenger.DepositForBurn( - s_mockUSDC.s_nonce(), - address(s_token), - amount, - address(s_usdcTokenPoolWithAllowList), - destinationReceiver, - expectedDomain.domainIdentifier, - s_mockUSDC.DESTINATION_TOKEN_MESSENGER(), - expectedDomain.allowedCaller - ); - vm.expectEmit(); - emit TokenPool.Burned(s_routerAllowedOnRamp, amount); - - Pool.LockOrBurnOutV1 memory poolReturnDataV1 = s_usdcTokenPoolWithAllowList.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: s_allowedList[0], - receiver: abi.encodePacked(destinationReceiver), - amount: amount, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - uint64 nonce = abi.decode(poolReturnDataV1.destPoolData, (uint64)); - assertEq(s_mockUSDC.s_nonce() - 1, nonce); - assertEq(poolReturnDataV1.destTokenAddress, abi.encode(DEST_CHAIN_USDC_TOKEN)); - } - - // Reverts - function test_UnknownDomain_Revert() public { - uint64 wrongDomain = DEST_CHAIN_SELECTOR + 1; - // We need to setup the wrong chainSelector so it reaches the domain check - Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1); - onRampUpdates[0] = Router.OnRamp({destChainSelector: wrongDomain, onRamp: s_routerAllowedOnRamp}); - s_router.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), new Router.OffRamp[](0)); - - TokenPool.ChainUpdate[] memory chainUpdates = new TokenPool.ChainUpdate[](1); - chainUpdates[0] = TokenPool.ChainUpdate({ - remoteChainSelector: wrongDomain, - remotePoolAddress: abi.encode(address(1)), - remoteTokenAddress: abi.encode(address(2)), - allowed: true, - outboundRateLimiterConfig: _getOutboundRateLimiterConfig(), - inboundRateLimiterConfig: _getInboundRateLimiterConfig() - }); - - s_usdcTokenPool.applyChainUpdates(chainUpdates); - - uint256 amount = 1000; - vm.startPrank(s_routerAllowedOnRamp); - deal(address(s_token), s_routerAllowedOnRamp, amount); - s_token.approve(address(s_usdcTokenPool), amount); - - vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.UnknownDomain.selector, wrongDomain)); - - s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(address(0)), - amount: amount, - remoteChainSelector: wrongDomain, - localToken: address(s_token) - }) - ); - } - - function test_CallerIsNotARampOnRouter_Revert() public { - vm.expectRevert(abi.encodeWithSelector(TokenPool.CallerIsNotARampOnRouter.selector, OWNER)); - - s_usdcTokenPool.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: OWNER, - receiver: abi.encodePacked(address(0)), - amount: 0, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - } - - function test_LockOrBurnWithAllowList_Revert() public { - vm.startPrank(s_routerAllowedOnRamp); - - vm.expectRevert(abi.encodeWithSelector(TokenPool.SenderNotAllowed.selector, STRANGER)); - - s_usdcTokenPoolWithAllowList.lockOrBurn( - Pool.LockOrBurnInV1({ - originalSender: STRANGER, - receiver: abi.encodePacked(address(0)), - amount: 1000, - remoteChainSelector: DEST_CHAIN_SELECTOR, - localToken: address(s_token) - }) - ); - } -} - -contract USDCTokenPool_releaseOrMint is USDCTokenPoolSetup { - // From https://github.com/circlefin/evm-cctp-contracts/blob/377c9bd813fb86a42d900ae4003599d82aef635a/src/messages/BurnMessage.sol#L57 - function _formatMessage( - uint32 _version, - bytes32 _burnToken, - bytes32 _mintRecipient, - uint256 _amount, - bytes32 _messageSender - ) internal pure returns (bytes memory) { - return abi.encodePacked(_version, _burnToken, _mintRecipient, _amount, _messageSender); - } - - function test_Fuzz_ReleaseOrMint_Success(address recipient, uint256 amount) public { - vm.assume(recipient != address(0) && recipient != address(s_token)); - amount = bound(amount, 0, _getInboundRateLimiterConfig().capacity); - - USDCMessage memory usdcMessage = USDCMessage({ - version: 0, - sourceDomain: SOURCE_DOMAIN_IDENTIFIER, - destinationDomain: DEST_DOMAIN_IDENTIFIER, - nonce: 0x060606060606, - sender: SOURCE_CHAIN_TOKEN_SENDER, - recipient: bytes32(uint256(uint160(recipient))), - destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), - messageBody: _formatMessage( - 0, - bytes32(uint256(uint160(address(s_token)))), - bytes32(uint256(uint160(recipient))), - amount, - bytes32(uint256(uint160(OWNER))) - ) - }); - - bytes memory message = _generateUSDCMessage(usdcMessage); - bytes memory attestation = bytes("attestation bytes"); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode( - USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: SOURCE_DOMAIN_IDENTIFIER}) - ), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - bytes memory offchainTokenData = - abi.encode(USDCTokenPool.MessageAndAttestation({message: message, attestation: attestation})); - - // The mocked receiver does not release the token to the pool, so we manually do it here - deal(address(s_token), address(s_usdcTokenPool), amount); - - vm.expectEmit(); - emit TokenPool.Minted(s_routerAllowedOffRamp, recipient, amount); - - vm.expectCall( - address(s_mockUSDCTransmitter), - abi.encodeWithSelector(MockE2EUSDCTransmitter.receiveMessage.selector, message, attestation) - ); - - vm.startPrank(s_routerAllowedOffRamp); - s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData - }) - ); - } - - // https://etherscan.io/tx/0xac9f501fe0b76df1f07a22e1db30929fd12524bc7068d74012dff948632f0883 - function test_ReleaseOrMintRealTx_Success() public { - bytes memory encodedUsdcMessage = - hex"000000000000000300000000000000000000127a00000000000000000000000019330d10d9cc8751218eaf51e8885d058642e08a000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e58310000000000000000000000004af08f56978be7dce2d1be3c65c005b41e79401c000000000000000000000000000000000000000000000000000000002057ff7a0000000000000000000000003a23f943181408eac424116af7b7790c94cb97a50000000000000000000000000000000000000000000000000000000000000000000000000000008274119237535fd659626b090f87e365ff89ebc7096bb32e8b0e85f155626b73ae7c4bb2485c184b7cc3cf7909045487890b104efb62ae74a73e32901bdcec91df1bb9ee08ccb014fcbcfe77b74d1263fd4e0b0e8de05d6c9a5913554364abfd5ea768b222f50c715908183905d74044bb2b97527c7e70ae7983c443a603557cac3b1c000000000000000000000000000000000000000000000000000000000000"; - bytes memory attestation = bytes("attestation bytes"); - - uint32 nonce = 4730; - uint32 sourceDomain = 3; - uint256 amount = 100; - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: nonce, sourceDomain: sourceDomain})), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - // The mocked receiver does not release the token to the pool, so we manually do it here - deal(address(s_token), address(s_usdcTokenPool), amount); - - bytes memory offchainTokenData = - abi.encode(USDCTokenPool.MessageAndAttestation({message: encodedUsdcMessage, attestation: attestation})); - - vm.expectCall( - address(s_mockUSDCTransmitter), - abi.encodeWithSelector(MockE2EUSDCTransmitter.receiveMessage.selector, encodedUsdcMessage, attestation) - ); - - vm.startPrank(s_routerAllowedOffRamp); - s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: OWNER, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData - }) - ); - } - - // Reverts - function test_UnlockingUSDCFailed_Revert() public { - vm.startPrank(s_routerAllowedOffRamp); - s_mockUSDCTransmitter.setShouldSucceed(false); - - uint256 amount = 13255235235; - - USDCMessage memory usdcMessage = USDCMessage({ - version: 0, - sourceDomain: SOURCE_DOMAIN_IDENTIFIER, - destinationDomain: DEST_DOMAIN_IDENTIFIER, - nonce: 0x060606060606, - sender: SOURCE_CHAIN_TOKEN_SENDER, - recipient: bytes32(uint256(uint160(address(s_mockUSDC)))), - destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), - messageBody: _formatMessage( - 0, - bytes32(uint256(uint160(address(s_token)))), - bytes32(uint256(uint160(OWNER))), - amount, - bytes32(uint256(uint160(OWNER))) - ) - }); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode( - USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: SOURCE_DOMAIN_IDENTIFIER}) - ), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - bytes memory offchainTokenData = abi.encode( - USDCTokenPool.MessageAndAttestation({message: _generateUSDCMessage(usdcMessage), attestation: bytes("")}) - ); - - vm.expectRevert(USDCTokenPool.UnlockingUSDCFailed.selector); - - s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: OWNER, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData - }) - ); - } - - function test_TokenMaxCapacityExceeded_Revert() public { - uint256 capacity = _getInboundRateLimiterConfig().capacity; - uint256 amount = 10 * capacity; - address recipient = address(1); - vm.startPrank(s_routerAllowedOffRamp); - - Internal.SourceTokenData memory sourceTokenData = Internal.SourceTokenData({ - sourcePoolAddress: abi.encode(SOURCE_CHAIN_USDC_POOL), - destTokenAddress: abi.encode(address(s_usdcTokenPool)), - extraData: abi.encode(USDCTokenPool.SourceTokenDataPayload({nonce: 1, sourceDomain: SOURCE_DOMAIN_IDENTIFIER})), - destGasAmount: USDC_DEST_TOKEN_GAS - }); - - bytes memory offchainTokenData = - abi.encode(USDCTokenPool.MessageAndAttestation({message: bytes(""), attestation: bytes("")})); - - vm.expectRevert( - abi.encodeWithSelector(RateLimiter.TokenMaxCapacityExceeded.selector, capacity, amount, address(s_token)) - ); - - s_usdcTokenPool.releaseOrMint( - Pool.ReleaseOrMintInV1({ - originalSender: abi.encode(OWNER), - receiver: recipient, - amount: amount, - localToken: address(s_token), - remoteChainSelector: SOURCE_CHAIN_SELECTOR, - sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: sourceTokenData.extraData, - offchainTokenData: offchainTokenData - }) - ); - } -} - -contract USDCTokenPool_supportsInterface is USDCTokenPoolSetup { - function test_SupportsInterface_Success() public view { - assertTrue(s_usdcTokenPool.supportsInterface(type(IPoolV1).interfaceId)); - assertTrue(s_usdcTokenPool.supportsInterface(type(IERC165).interfaceId)); - } -} - -contract USDCTokenPool_setDomains is USDCTokenPoolSetup { - mapping(uint64 destChainSelector => USDCTokenPool.Domain domain) private s_chainToDomain; - - // Setting lower fuzz run as 256 runs was causing differing gas results in snapshot. - /// forge-config: default.fuzz.runs = 32 - /// forge-config: ccip.fuzz.runs = 32 - function test_Fuzz_SetDomains_Success( - bytes32[5] calldata allowedCallers, - uint32[5] calldata domainIdentifiers, - uint64[5] calldata destChainSelectors - ) public { - uint256 numberOfDomains = allowedCallers.length; - USDCTokenPool.DomainUpdate[] memory domainUpdates = new USDCTokenPool.DomainUpdate[](numberOfDomains); - for (uint256 i = 0; i < numberOfDomains; ++i) { - vm.assume(allowedCallers[i] != bytes32(0) && domainIdentifiers[i] != 0 && destChainSelectors[i] != 0); - - domainUpdates[i] = USDCTokenPool.DomainUpdate({ - allowedCaller: allowedCallers[i], - domainIdentifier: domainIdentifiers[i], - destChainSelector: destChainSelectors[i], - enabled: true - }); - - s_chainToDomain[destChainSelectors[i]] = - USDCTokenPool.Domain({domainIdentifier: domainIdentifiers[i], allowedCaller: allowedCallers[i], enabled: true}); - } - - vm.expectEmit(); - emit USDCTokenPool.DomainsSet(domainUpdates); - - s_usdcTokenPool.setDomains(domainUpdates); - - for (uint256 i = 0; i < numberOfDomains; ++i) { - USDCTokenPool.Domain memory expected = s_chainToDomain[destChainSelectors[i]]; - USDCTokenPool.Domain memory got = s_usdcTokenPool.getDomain(destChainSelectors[i]); - assertEq(got.allowedCaller, expected.allowedCaller); - assertEq(got.domainIdentifier, expected.domainIdentifier); - } - } - - // Reverts - - function test_OnlyOwner_Revert() public { - USDCTokenPool.DomainUpdate[] memory domainUpdates = new USDCTokenPool.DomainUpdate[](0); - - vm.startPrank(STRANGER); - vm.expectRevert(Ownable2Step.OnlyCallableByOwner.selector); - - s_usdcTokenPool.setDomains(domainUpdates); - } - - function test_InvalidDomain_Revert() public { - bytes32 validCaller = bytes32(uint256(25)); - // Ensure valid domain works - USDCTokenPool.DomainUpdate[] memory domainUpdates = new USDCTokenPool.DomainUpdate[](1); - domainUpdates[0] = USDCTokenPool.DomainUpdate({ - allowedCaller: validCaller, - domainIdentifier: 0, // ensures 0 is valid, as this is eth mainnet - destChainSelector: 45690, - enabled: true - }); - - s_usdcTokenPool.setDomains(domainUpdates); - - // Make update invalid on allowedCaller - domainUpdates[0].allowedCaller = bytes32(0); - vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidDomain.selector, domainUpdates[0])); - - s_usdcTokenPool.setDomains(domainUpdates); - - // Make valid again - domainUpdates[0].allowedCaller = validCaller; - - // Make invalid on destChainSelector - domainUpdates[0].destChainSelector = 0; - vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidDomain.selector, domainUpdates[0])); - - s_usdcTokenPool.setDomains(domainUpdates); - } -} - -contract USDCTokenPool__validateMessage is USDCTokenPoolSetup { - function test_Fuzz_ValidateMessage_Success(uint32 sourceDomain, uint64 nonce) public { - vm.pauseGasMetering(); - USDCMessage memory usdcMessage = USDCMessage({ - version: 0, - sourceDomain: sourceDomain, - destinationDomain: DEST_DOMAIN_IDENTIFIER, - nonce: nonce, - sender: SOURCE_CHAIN_TOKEN_SENDER, - recipient: bytes32(uint256(299999)), - destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), - messageBody: bytes("") - }); - - bytes memory encodedUsdcMessage = _generateUSDCMessage(usdcMessage); - - vm.resumeGasMetering(); - s_usdcTokenPool.validateMessage( - encodedUsdcMessage, USDCTokenPool.SourceTokenDataPayload({nonce: nonce, sourceDomain: sourceDomain}) - ); - } - - // Reverts - - function test_ValidateInvalidMessage_Revert() public { - USDCMessage memory usdcMessage = USDCMessage({ - version: 0, - sourceDomain: 1553252, - destinationDomain: DEST_DOMAIN_IDENTIFIER, - nonce: 387289284924, - sender: SOURCE_CHAIN_TOKEN_SENDER, - recipient: bytes32(uint256(92398429395823)), - destinationCaller: bytes32(uint256(uint160(address(s_usdcTokenPool)))), - messageBody: bytes("") - }); - - USDCTokenPool.SourceTokenDataPayload memory sourceTokenData = - USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: usdcMessage.sourceDomain}); - - bytes memory encodedUsdcMessage = _generateUSDCMessage(usdcMessage); - - s_usdcTokenPool.validateMessage(encodedUsdcMessage, sourceTokenData); - - uint32 expectedSourceDomain = usdcMessage.sourceDomain + 1; - - vm.expectRevert( - abi.encodeWithSelector(USDCTokenPool.InvalidSourceDomain.selector, expectedSourceDomain, usdcMessage.sourceDomain) - ); - s_usdcTokenPool.validateMessage( - encodedUsdcMessage, - USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: expectedSourceDomain}) - ); - - uint64 expectedNonce = usdcMessage.nonce + 1; - - vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidNonce.selector, expectedNonce, usdcMessage.nonce)); - s_usdcTokenPool.validateMessage( - encodedUsdcMessage, - USDCTokenPool.SourceTokenDataPayload({nonce: expectedNonce, sourceDomain: usdcMessage.sourceDomain}) - ); - - usdcMessage.destinationDomain = DEST_DOMAIN_IDENTIFIER + 1; - vm.expectRevert( - abi.encodeWithSelector( - USDCTokenPool.InvalidDestinationDomain.selector, DEST_DOMAIN_IDENTIFIER, usdcMessage.destinationDomain - ) - ); - - s_usdcTokenPool.validateMessage( - _generateUSDCMessage(usdcMessage), - USDCTokenPool.SourceTokenDataPayload({nonce: usdcMessage.nonce, sourceDomain: usdcMessage.sourceDomain}) - ); - usdcMessage.destinationDomain = DEST_DOMAIN_IDENTIFIER; - - uint32 wrongVersion = usdcMessage.version + 1; - - usdcMessage.version = wrongVersion; - encodedUsdcMessage = _generateUSDCMessage(usdcMessage); - - vm.expectRevert(abi.encodeWithSelector(USDCTokenPool.InvalidMessageVersion.selector, wrongVersion)); - s_usdcTokenPool.validateMessage(encodedUsdcMessage, sourceTokenData); - } -} diff --git a/contracts/src/v0.8/ccip/test/rateLimiter/MultiAggregateRateLimiter.t.sol b/contracts/src/v0.8/ccip/test/rateLimiter/MultiAggregateRateLimiter.t.sol index 42edde7e162..893656c9c8b 100644 --- a/contracts/src/v0.8/ccip/test/rateLimiter/MultiAggregateRateLimiter.t.sol +++ b/contracts/src/v0.8/ccip/test/rateLimiter/MultiAggregateRateLimiter.t.sol @@ -17,17 +17,17 @@ import {Vm} from "forge-std/Vm.sol"; contract MultiAggregateRateLimiterSetup is BaseTest, FeeQuoterSetup { MultiAggregateRateLimiterHelper internal s_rateLimiter; - address internal immutable TOKEN = 0x21118E64E1fB0c487F25Dd6d3601FF6af8D32E4e; + address internal constant TOKEN = 0x21118E64E1fB0c487F25Dd6d3601FF6af8D32E4e; uint224 internal constant TOKEN_PRICE = 4e18; uint64 internal constant CHAIN_SELECTOR_1 = 5009297550715157269; uint64 internal constant CHAIN_SELECTOR_2 = 4949039107694359620; - RateLimiter.Config internal RATE_LIMITER_CONFIG_1 = RateLimiter.Config({isEnabled: true, rate: 5, capacity: 100}); - RateLimiter.Config internal RATE_LIMITER_CONFIG_2 = RateLimiter.Config({isEnabled: true, rate: 10, capacity: 200}); + RateLimiter.Config internal s_rateLimiterConfig1 = RateLimiter.Config({isEnabled: true, rate: 5, capacity: 100}); + RateLimiter.Config internal s_rateLimiterConfig2 = RateLimiter.Config({isEnabled: true, rate: 10, capacity: 200}); - address internal immutable MOCK_OFFRAMP = address(1111); - address internal immutable MOCK_ONRAMP = address(1112); + address internal constant MOCK_OFFRAMP = address(1111); + address internal constant MOCK_ONRAMP = address(1112); address[] internal s_authorizedCallers; @@ -43,22 +43,22 @@ contract MultiAggregateRateLimiterSetup is BaseTest, FeeQuoterSetup { configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); configUpdates[1] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_2, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_2 + rateLimiterConfig: s_rateLimiterConfig2 }); configUpdates[2] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1, isOutboundLane: true, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); configUpdates[3] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_2, isOutboundLane: true, - rateLimiterConfig: RATE_LIMITER_CONFIG_2 + rateLimiterConfig: s_rateLimiterConfig2 }); s_authorizedCallers = new address[](2); @@ -169,32 +169,32 @@ contract MultiAggregateRateLimiter_setFeeQuoter is MultiAggregateRateLimiterSetu contract MultiAggregateRateLimiter_getTokenBucket is MultiAggregateRateLimiterSetup { function test_GetTokenBucket_Success() public view { RateLimiter.TokenBucket memory bucketInbound = s_rateLimiter.currentRateLimiterState(CHAIN_SELECTOR_1, false); - _assertConfigWithTokenBucketEquality(RATE_LIMITER_CONFIG_1, bucketInbound); + _assertConfigWithTokenBucketEquality(s_rateLimiterConfig1, bucketInbound); assertEq(BLOCK_TIME, bucketInbound.lastUpdated); RateLimiter.TokenBucket memory bucketOutbound = s_rateLimiter.currentRateLimiterState(CHAIN_SELECTOR_1, true); - _assertConfigWithTokenBucketEquality(RATE_LIMITER_CONFIG_1, bucketOutbound); + _assertConfigWithTokenBucketEquality(s_rateLimiterConfig1, bucketOutbound); assertEq(BLOCK_TIME, bucketOutbound.lastUpdated); } function test_Refill_Success() public { - RATE_LIMITER_CONFIG_1.capacity = RATE_LIMITER_CONFIG_1.capacity * 2; + s_rateLimiterConfig1.capacity = s_rateLimiterConfig1.capacity * 2; MultiAggregateRateLimiter.RateLimiterConfigArgs[] memory configUpdates = new MultiAggregateRateLimiter.RateLimiterConfigArgs[](1); configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); s_rateLimiter.applyRateLimiterConfigUpdates(configUpdates); RateLimiter.TokenBucket memory bucket = s_rateLimiter.currentRateLimiterState(CHAIN_SELECTOR_1, false); - assertEq(RATE_LIMITER_CONFIG_1.rate, bucket.rate); - assertEq(RATE_LIMITER_CONFIG_1.capacity, bucket.capacity); - assertEq(RATE_LIMITER_CONFIG_1.capacity / 2, bucket.tokens); + assertEq(s_rateLimiterConfig1.rate, bucket.rate); + assertEq(s_rateLimiterConfig1.capacity, bucket.capacity); + assertEq(s_rateLimiterConfig1.capacity / 2, bucket.tokens); assertEq(BLOCK_TIME, bucket.lastUpdated); uint256 warpTime = 4; @@ -202,16 +202,16 @@ contract MultiAggregateRateLimiter_getTokenBucket is MultiAggregateRateLimiterSe bucket = s_rateLimiter.currentRateLimiterState(CHAIN_SELECTOR_1, false); - assertEq(RATE_LIMITER_CONFIG_1.rate, bucket.rate); - assertEq(RATE_LIMITER_CONFIG_1.capacity, bucket.capacity); - assertEq(RATE_LIMITER_CONFIG_1.capacity / 2 + warpTime * RATE_LIMITER_CONFIG_1.rate, bucket.tokens); + assertEq(s_rateLimiterConfig1.rate, bucket.rate); + assertEq(s_rateLimiterConfig1.capacity, bucket.capacity); + assertEq(s_rateLimiterConfig1.capacity / 2 + warpTime * s_rateLimiterConfig1.rate, bucket.tokens); assertEq(BLOCK_TIME + warpTime, bucket.lastUpdated); vm.warp(BLOCK_TIME + warpTime * 100); // Bucket overflow bucket = s_rateLimiter.currentRateLimiterState(CHAIN_SELECTOR_1, false); - assertEq(RATE_LIMITER_CONFIG_1.capacity, bucket.tokens); + assertEq(s_rateLimiterConfig1.capacity, bucket.tokens); } // Reverts @@ -242,7 +242,7 @@ contract MultiAggregateRateLimiter_applyRateLimiterConfigUpdates is MultiAggrega configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1 + 1, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); vm.expectEmit(); @@ -268,7 +268,7 @@ contract MultiAggregateRateLimiter_applyRateLimiterConfigUpdates is MultiAggrega configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1 + 1, isOutboundLane: true, - rateLimiterConfig: RATE_LIMITER_CONFIG_2 + rateLimiterConfig: s_rateLimiterConfig2 }); vm.expectEmit(); @@ -356,7 +356,7 @@ contract MultiAggregateRateLimiter_applyRateLimiterConfigUpdates is MultiAggrega configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_2 + rateLimiterConfig: s_rateLimiterConfig2 }); RateLimiter.TokenBucket memory bucket1 = @@ -382,7 +382,7 @@ contract MultiAggregateRateLimiter_applyRateLimiterConfigUpdates is MultiAggrega // Outbound lane config remains unchanged _assertConfigWithTokenBucketEquality( - RATE_LIMITER_CONFIG_1, s_rateLimiter.currentRateLimiterState(CHAIN_SELECTOR_1, true) + s_rateLimiterConfig1, s_rateLimiter.currentRateLimiterState(CHAIN_SELECTOR_1, true) ); } @@ -392,7 +392,7 @@ contract MultiAggregateRateLimiter_applyRateLimiterConfigUpdates is MultiAggrega configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); RateLimiter.TokenBucket memory bucketPreUpdate = @@ -420,7 +420,7 @@ contract MultiAggregateRateLimiter_applyRateLimiterConfigUpdates is MultiAggrega configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: 0, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); vm.expectRevert(MultiAggregateRateLimiter.ZeroChainSelectorNotAllowed.selector); @@ -433,7 +433,7 @@ contract MultiAggregateRateLimiter_applyRateLimiterConfigUpdates is MultiAggrega configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1 + 1, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); vm.startPrank(STRANGER); @@ -740,7 +740,7 @@ contract MultiAggregateRateLimiter_updateRateLimitTokens is MultiAggregateRateLi } contract MultiAggregateRateLimiter_onInboundMessage is MultiAggregateRateLimiterSetup { - address internal immutable MOCK_RECEIVER = address(1113); + address internal constant MOCK_RECEIVER = address(1113); function setUp() public virtual override { super.setUp(); @@ -815,7 +815,7 @@ contract MultiAggregateRateLimiter_onInboundMessage is MultiAggregateRateLimiter configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1, isOutboundLane: false, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); configUpdates[0].rateLimiterConfig.isEnabled = false; @@ -1050,7 +1050,7 @@ contract MultiAggregateRateLimiter_onOutboundMessage is MultiAggregateRateLimite configUpdates[0] = MultiAggregateRateLimiter.RateLimiterConfigArgs({ remoteChainSelector: CHAIN_SELECTOR_1, isOutboundLane: true, - rateLimiterConfig: RATE_LIMITER_CONFIG_1 + rateLimiterConfig: s_rateLimiterConfig1 }); configUpdates[0].rateLimiterConfig.isEnabled = false; diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol index 280f158da4d..449725317a1 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNHome.t.sol @@ -2,10 +2,8 @@ pragma solidity 0.8.24; import {Ownable2Step} from "../../../shared/access/Ownable2Step.sol"; -import {Internal} from "../../libraries/Internal.sol"; import {RMNHome} from "../../rmn/RMNHome.sol"; import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; contract RMNHomeTest is Test { struct Config { diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNRemote.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNRemote.t.sol index 5f8f250b6e1..b9411d2e3a9 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNRemote.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNRemote.t.sol @@ -183,16 +183,16 @@ contract RMNRemote_curse is RMNRemoteSetup { s_rmnRemote.curse(s_curseSubjects); assertEq(abi.encode(s_rmnRemote.getCursedSubjects()), abi.encode(s_curseSubjects)); - assertTrue(s_rmnRemote.isCursed(curseSubj1)); - assertTrue(s_rmnRemote.isCursed(curseSubj2)); + assertTrue(s_rmnRemote.isCursed(CURSE_SUBJ_1)); + assertTrue(s_rmnRemote.isCursed(CURSE_SUBJ_2)); // Should not have cursed a random subject assertFalse(s_rmnRemote.isCursed(bytes16(keccak256("subject 3")))); } function test_curse_AlreadyCursed_duplicateSubject_reverts() public { - s_curseSubjects.push(curseSubj1); + s_curseSubjects.push(CURSE_SUBJ_1); - vm.expectRevert(abi.encodeWithSelector(RMNRemote.AlreadyCursed.selector, curseSubj1)); + vm.expectRevert(abi.encodeWithSelector(RMNRemote.AlreadyCursed.selector, CURSE_SUBJ_1)); s_rmnRemote.curse(s_curseSubjects); } @@ -217,14 +217,14 @@ contract RMNRemote_uncurse is RMNRemoteSetup { s_rmnRemote.uncurse(s_curseSubjects); assertEq(s_rmnRemote.getCursedSubjects().length, 0); - assertFalse(s_rmnRemote.isCursed(curseSubj1)); - assertFalse(s_rmnRemote.isCursed(curseSubj2)); + assertFalse(s_rmnRemote.isCursed(CURSE_SUBJ_1)); + assertFalse(s_rmnRemote.isCursed(CURSE_SUBJ_2)); } function test_uncurse_NotCursed_duplicatedUncurseSubject_reverts() public { - s_curseSubjects.push(curseSubj1); + s_curseSubjects.push(CURSE_SUBJ_1); - vm.expectRevert(abi.encodeWithSelector(RMNRemote.NotCursed.selector, curseSubj1)); + vm.expectRevert(abi.encodeWithSelector(RMNRemote.NotCursed.selector, CURSE_SUBJ_1)); s_rmnRemote.uncurse(s_curseSubjects); } diff --git a/contracts/src/v0.8/ccip/test/rmn/RMNRemoteSetup.t.sol b/contracts/src/v0.8/ccip/test/rmn/RMNRemoteSetup.t.sol index 435fa76cce0..88af28e992c 100644 --- a/contracts/src/v0.8/ccip/test/rmn/RMNRemoteSetup.t.sol +++ b/contracts/src/v0.8/ccip/test/rmn/RMNRemoteSetup.t.sol @@ -7,8 +7,6 @@ import {RMNRemote} from "../../rmn/RMNRemote.sol"; import {BaseTest} from "../BaseTest.t.sol"; import {Vm} from "forge-std/Vm.sol"; -import "forge-std/console.sol"; - contract RMNRemoteSetup is BaseTest { RMNRemote public s_rmnRemote; address public OFF_RAMP_ADDRESS; @@ -16,18 +14,18 @@ contract RMNRemoteSetup is BaseTest { RMNRemote.Signer[] public s_signers; Vm.Wallet[] public s_signerWallets; - Internal.MerkleRoot[] s_merkleRoots; - IRMNRemote.Signature[] s_signatures; + Internal.MerkleRoot[] internal s_merkleRoots; + IRMNRemote.Signature[] internal s_signatures; - bytes16 internal constant curseSubj1 = bytes16(keccak256("subject 1")); - bytes16 internal constant curseSubj2 = bytes16(keccak256("subject 2")); + bytes16 internal constant CURSE_SUBJ_1 = bytes16(keccak256("subject 1")); + bytes16 internal constant CURSE_SUBJ_2 = bytes16(keccak256("subject 2")); bytes16[] internal s_curseSubjects; function setUp() public virtual override { super.setUp(); s_rmnRemote = new RMNRemote(1); OFF_RAMP_ADDRESS = makeAddr("OFF RAMP"); - s_curseSubjects = [curseSubj1, curseSubj2]; + s_curseSubjects = [CURSE_SUBJ_1, CURSE_SUBJ_2]; _setupSigners(10); } @@ -46,13 +44,13 @@ contract RMNRemoteSetup is BaseTest { s_signers.pop(); } - for (uint256 i = 0; i < numSigners; i++) { + for (uint256 i = 0; i < numSigners; ++i) { s_signerWallets.push(vm.createWallet(_randomNum())); } _sort(s_signerWallets); - for (uint256 i = 0; i < numSigners; i++) { + for (uint256 i = 0; i < numSigners; ++i) { s_signers.push(RMNRemote.Signer({onchainPublicKey: s_signerWallets[i].addr, nodeIndex: uint64(i)})); } } @@ -60,8 +58,8 @@ contract RMNRemoteSetup is BaseTest { /// @notice generates n merkleRoots and matching valid signatures and populates them into /// the shared storage vars function _generatePayloadAndSigs(uint256 numUpdates, uint256 numSigs) internal { - require(numUpdates > 0, "need at least 1 dest lane update"); - require(numSigs <= s_signerWallets.length, "cannot generate more sigs than signers"); + vm.assertTrue(numUpdates > 0, "need at least 1 dest lane update"); + vm.assertTrue(numSigs <= s_signerWallets.length, "cannot generate more sigs than signers"); // remove any existing merkleRoots and sigs while (s_merkleRoots.length > 0) { diff --git a/contracts/src/v0.8/ccip/test/router/Router.t.sol b/contracts/src/v0.8/ccip/test/router/Router.t.sol index b5cfd6cfbec..9b2bb92b4e4 100644 --- a/contracts/src/v0.8/ccip/test/router/Router.t.sol +++ b/contracts/src/v0.8/ccip/test/router/Router.t.sol @@ -11,8 +11,8 @@ import {Client} from "../../libraries/Client.sol"; import {Internal} from "../../libraries/Internal.sol"; import {OnRamp} from "../../onRamp/OnRamp.sol"; import {MaybeRevertMessageReceiver} from "../helpers/receivers/MaybeRevertMessageReceiver.sol"; -import {OffRampSetup} from "../offRamp/OffRampSetup.t.sol"; -import {OnRampSetup} from "../onRamp/OnRampSetup.t.sol"; +import {OffRampSetup} from "../offRamp/offRamp/OffRampSetup.t.sol"; +import {OnRampSetup} from "../onRamp/onRamp/OnRampSetup.t.sol"; import {RouterSetup} from "../router/RouterSetup.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; diff --git a/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol b/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol index dfb599bd307..cf40fb62d22 100644 --- a/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol +++ b/contracts/src/v0.8/ccip/test/tokenAdminRegistry/RegistryModuleOwnerCustom.t.sol @@ -8,6 +8,7 @@ import {RegistryModuleOwnerCustom} from "../../tokenAdminRegistry/RegistryModule import {TokenAdminRegistry} from "../../tokenAdminRegistry/TokenAdminRegistry.sol"; import {BurnMintERC677Helper} from "../helpers/BurnMintERC677Helper.sol"; +import {AccessControl} from "../../../vendor/openzeppelin-solidity/v5.0.2/contracts/access/AccessControl.sol"; import {Test} from "forge-std/Test.sol"; contract RegistryModuleOwnerCustomSetup is Test { @@ -102,3 +103,54 @@ contract RegistryModuleOwnerCustom_registerAdminViaOwner is RegistryModuleOwnerC s_registryModuleOwnerCustom.registerAdminViaOwner(s_token); } } + +contract AccessController is AccessControl { + constructor( + address admin + ) { + _grantRole(DEFAULT_ADMIN_ROLE, admin); + } +} + +contract RegistryModuleOwnerCustom_registerAccessControlDefaultAdmin is RegistryModuleOwnerCustomSetup { + function setUp() public override { + super.setUp(); + + s_token = address(new AccessController(OWNER)); + } + + function test_registerAccessControlDefaultAdmin_Success() public { + assertEq(s_tokenAdminRegistry.getTokenConfig(s_token).administrator, address(0)); + + bytes32 defaultAdminRole = AccessController(s_token).DEFAULT_ADMIN_ROLE(); + + vm.expectCall(address(s_token), abi.encodeWithSelector(AccessControl.hasRole.selector, defaultAdminRole, OWNER), 1); + vm.expectCall( + address(s_tokenAdminRegistry), + abi.encodeWithSelector(TokenAdminRegistry.proposeAdministrator.selector, s_token, OWNER), + 1 + ); + + vm.expectEmit(); + emit RegistryModuleOwnerCustom.AdministratorRegistered(s_token, OWNER); + + s_registryModuleOwnerCustom.registerAccessControlDefaultAdmin(s_token); + + assertEq(s_tokenAdminRegistry.getTokenConfig(s_token).pendingAdministrator, OWNER); + } + + function test_registerAccessControlDefaultAdmin_Revert() public { + bytes32 defaultAdminRole = AccessController(s_token).DEFAULT_ADMIN_ROLE(); + + address wrongSender = makeAddr("Not_expected_owner"); + vm.startPrank(wrongSender); + + vm.expectRevert( + abi.encodeWithSelector( + RegistryModuleOwnerCustom.RequiredRoleNotFound.selector, wrongSender, defaultAdminRole, s_token + ) + ); + + s_registryModuleOwnerCustom.registerAccessControlDefaultAdmin(s_token); + } +} diff --git a/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol b/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol index dd2c82fe3dc..4392fa8c56f 100644 --- a/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol +++ b/contracts/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol @@ -6,13 +6,16 @@ import {IGetCCIPAdmin} from "../interfaces/IGetCCIPAdmin.sol"; import {IOwner} from "../interfaces/IOwner.sol"; import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol"; +import {AccessControl} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/access/AccessControl.sol"; + contract RegistryModuleOwnerCustom is ITypeAndVersion { error CanOnlySelfRegister(address admin, address token); + error RequiredRoleNotFound(address msgSender, bytes32 role, address token); error AddressZero(); event AdministratorRegistered(address indexed token, address indexed administrator); - string public constant override typeAndVersion = "RegistryModuleOwnerCustom 1.5.0"; + string public constant override typeAndVersion = "RegistryModuleOwnerCustom 1.6.0"; // The TokenAdminRegistry contract ITokenAdminRegistry internal immutable i_tokenAdminRegistry; @@ -44,6 +47,20 @@ contract RegistryModuleOwnerCustom is ITypeAndVersion { _registerAdmin(token, IOwner(token).owner()); } + /// @notice Registers the admin of the token using OZ's AccessControl DEFAULT_ADMIN_ROLE. + /// @param token The token to register the admin for. + /// @dev The caller must have the DEFAULT_ADMIN_ROLE as defined by the contract itself. + function registerAccessControlDefaultAdmin( + address token + ) external { + bytes32 defaultAdminRole = AccessControl(token).DEFAULT_ADMIN_ROLE(); + if (!AccessControl(token).hasRole(defaultAdminRole, msg.sender)) { + revert RequiredRoleNotFound(msg.sender, defaultAdminRole, token); + } + + _registerAdmin(token, msg.sender); + } + /// @notice Registers the admin of the token to msg.sender given that the /// admin is equal to msg.sender. /// @param token The token to register the admin for. diff --git a/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go b/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go index 9fda8e52393..39aa322e0a2 100644 --- a/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go +++ b/core/capabilities/ccip/ccip_integration_tests/ccipreader/ccipreader_test.go @@ -165,6 +165,17 @@ func TestCCIPReader_ExecutedMessageRanges(t *testing.T) { consts.EventNameExecutionStateChanged: { ChainSpecificName: consts.EventNameExecutionStateChanged, ReadType: evmtypes.Event, + EventDefinitions: &evmtypes.EventDefinitions{ + GenericTopicNames: map[string]string{ + "sourceChainSelector": consts.EventAttributeSourceChain, + "sequenceNumber": consts.EventAttributeSequenceNumber, + }, + GenericDataWordDetails: map[string]evmtypes.DataWordDetail{ + consts.EventAttributeState: { + Name: "state", + }, + }, + }, }, }, }, @@ -236,6 +247,13 @@ func TestCCIPReader_MsgsBetweenSeqNums(t *testing.T) { consts.EventNameCCIPMessageSent: { ChainSpecificName: "CCIPMessageSent", ReadType: evmtypes.Event, + EventDefinitions: &evmtypes.EventDefinitions{ + GenericDataWordDetails: map[string]evmtypes.DataWordDetail{ + consts.EventAttributeSourceChain: {Name: "message.header.sourceChainSelector"}, + consts.EventAttributeDestChain: {Name: "message.header.destChainSelector"}, + consts.EventAttributeSequenceNumber: {Name: "message.header.sequenceNumber"}, + }, + }, }, }, }, diff --git a/core/capabilities/ccip/configs/evm/contract_reader.go b/core/capabilities/ccip/configs/evm/contract_reader.go index 20bab0ac82a..7cbc4a9fa8d 100644 --- a/core/capabilities/ccip/configs/evm/contract_reader.go +++ b/core/capabilities/ccip/configs/evm/contract_reader.go @@ -95,6 +95,17 @@ var DestReaderConfig = evmrelaytypes.ChainReaderConfig{ consts.EventNameExecutionStateChanged: { ChainSpecificName: mustGetEventName(consts.EventNameExecutionStateChanged, offrampABI), ReadType: evmrelaytypes.Event, + EventDefinitions: &evmrelaytypes.EventDefinitions{ + GenericTopicNames: map[string]string{ + "sourceChainSelector": consts.EventAttributeSourceChain, + "sequenceNumber": consts.EventAttributeSequenceNumber, + }, + GenericDataWordDetails: map[string]evmrelaytypes.DataWordDetail{ + consts.EventAttributeState: { + Name: "state", + }, + }, + }, }, }, }, @@ -210,6 +221,13 @@ var SourceReaderConfig = evmrelaytypes.ChainReaderConfig{ consts.EventNameCCIPMessageSent: { ChainSpecificName: mustGetEventName("CCIPMessageSent", onrampABI), ReadType: evmrelaytypes.Event, + EventDefinitions: &evmrelaytypes.EventDefinitions{ + GenericDataWordDetails: map[string]evmrelaytypes.DataWordDetail{ + consts.EventAttributeSourceChain: {Name: "message.header.sourceChainSelector"}, + consts.EventAttributeDestChain: {Name: "message.header.destChainSelector"}, + consts.EventAttributeSequenceNumber: {Name: "message.header.sequenceNumber"}, + }, + }, }, consts.MethodNameOnRampGetStaticConfig: { ChainSpecificName: mustGetMethodName("getStaticConfig", onrampABI), diff --git a/core/capabilities/ccip/launcher/deployment.go b/core/capabilities/ccip/launcher/deployment.go index 64571183425..870c0409494 100644 --- a/core/capabilities/ccip/launcher/deployment.go +++ b/core/capabilities/ccip/launcher/deployment.go @@ -1,12 +1,11 @@ package launcher import ( - "errors" "fmt" - "sync" - + mapset "github.com/deckarep/golang-set/v2" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "go.uber.org/multierr" + "golang.org/x/exp/maps" + "golang.org/x/sync/errgroup" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" ) @@ -35,47 +34,54 @@ func (c pluginRegistry) CloseAll() error { // If any of the previous config digests are no longer present, we need to shut those down // We don't care about if they're exec/commit or active/candidate, that all happens in the plugin func (c pluginRegistry) TransitionFrom(prevPlugins pluginRegistry) error { - var allErrs error - if len(c) > MaxPlugins || len(prevPlugins) > MaxPlugins { return fmt.Errorf("current pluginRegistry or prevPlugins have more than 4 instances: len(prevPlugins): %d, len(currPlugins): %d", len(prevPlugins), len(c)) } - var wg sync.WaitGroup - var mu sync.Mutex - // This shuts down instances that were present previously, but are no longer needed - for digest, oracle := range prevPlugins { - if _, ok := c[digest]; !ok { - wg.Add(1) - go func(o cctypes.CCIPOracle) { - defer wg.Done() - if err := o.Close(); err != nil { - mu.Lock() - allErrs = multierr.Append(allErrs, err) - mu.Unlock() - } - }(oracle) - } + prevOracles := mapset.NewSet[ocrtypes.ConfigDigest](maps.Keys(prevPlugins)...) + currOracles := mapset.NewSet[ocrtypes.ConfigDigest](maps.Keys(c)...) + + var ops = make([]syncAction, 0, 2*MaxPlugins) + for digest := range prevOracles.Difference(currOracles).Iterator().C { + ops = append(ops, syncAction{ + command: closeAction, + oracle: prevPlugins[digest], + }) + } + + for digest := range currOracles.Difference(prevOracles).Iterator().C { + ops = append(ops, syncAction{ + command: openAction, + oracle: c[digest], + }) } - wg.Wait() - // This will start the instances that were not previously present, but are in the new config - for digest, oracle := range c { - if digest == [32]byte{} { - allErrs = multierr.Append(allErrs, errors.New("cannot start a plugin with an empty config digest")) - } else if _, ok := prevPlugins[digest]; !ok { - wg.Add(1) - go func(o cctypes.CCIPOracle) { - defer wg.Done() - if err := o.Start(); err != nil { - mu.Lock() - allErrs = multierr.Append(allErrs, err) - mu.Unlock() + g := new(errgroup.Group) + for _, op := range ops { + op := op + g.Go(func() error { + if op.command == closeAction { + if err := op.oracle.Close(); err != nil { + return err } - }(oracle) - } + } else if op.command == openAction { + if err := op.oracle.Start(); err != nil { + return err + } + } + return nil + }) } - wg.Wait() - return allErrs + return g.Wait() +} + +const ( + closeAction = iota + openAction +) + +type syncAction struct { + command int + oracle cctypes.CCIPOracle } diff --git a/core/capabilities/ccip/launcher/integration_test.go b/core/capabilities/ccip/launcher/integration_test.go index f0a4bd46bb3..954fda03969 100644 --- a/core/capabilities/ccip/launcher/integration_test.go +++ b/core/capabilities/ccip/launcher/integration_test.go @@ -1,6 +1,7 @@ package launcher import ( + "context" "testing" "time" @@ -115,7 +116,7 @@ type oracleCreatorPrints struct { t *testing.T } -func (o *oracleCreatorPrints) Create(_ uint32, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { +func (o *oracleCreatorPrints) Create(ctx context.Context, _ uint32, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { pluginType := cctypes.PluginType(config.Config.PluginType) o.t.Logf("Creating plugin oracle (pluginType: %s) with config %+v\n", pluginType, config) return &oraclePrints{pluginType: pluginType, config: config, t: o.t}, nil diff --git a/core/capabilities/ccip/launcher/launcher.go b/core/capabilities/ccip/launcher/launcher.go index 167ac0a815e..76a6c204058 100644 --- a/core/capabilities/ccip/launcher/launcher.go +++ b/core/capabilities/ccip/launcher/launcher.go @@ -68,7 +68,7 @@ type launcher struct { myP2PID ragep2ptypes.PeerID lggr logger.Logger homeChainReader ccipreader.HomeChain - stopChan chan struct{} + stopChan services.StopChan // latestState is the latest capability registry state received from the syncer. latestState registrysyncer.LocalRegistry // regState is the latest capability registry state that we have successfully processed. @@ -140,12 +140,16 @@ func (l *launcher) Start(context.Context) error { func (l *launcher) monitor() { defer l.wg.Done() ticker := time.NewTicker(l.tickInterval) + + ctx, cancel := l.stopChan.NewCtx() + defer cancel() + for { select { - case <-l.stopChan: + case <-ctx.Done(): return case <-ticker.C: - if err := l.tick(); err != nil { + if err := l.tick(ctx); err != nil { l.lggr.Errorw("Failed to tick", "err", err) } } @@ -154,7 +158,7 @@ func (l *launcher) monitor() { // tick gets the latest registry state and processes the diff between the current and latest state. // This may lead to starting or stopping OCR instances. -func (l *launcher) tick() error { +func (l *launcher) tick(ctx context.Context) error { // Ensure that the home chain reader is healthy. // For new jobs it may be possible that the home chain reader is not yet ready // so we won't be able to fetch configs and start any OCR instances. @@ -171,7 +175,7 @@ func (l *launcher) tick() error { return fmt.Errorf("failed to diff capability registry states: %w", err) } - err = l.processDiff(diffRes) + err = l.processDiff(ctx, diffRes) if err != nil { return fmt.Errorf("failed to process diff: %w", err) } @@ -183,17 +187,17 @@ func (l *launcher) tick() error { // for any added OCR instances, it will launch them. // for any removed OCR instances, it will shut them down. // for any updated OCR instances, it will restart them with the new configuration. -func (l *launcher) processDiff(diff diffResult) error { +func (l *launcher) processDiff(ctx context.Context, diff diffResult) error { err := l.processRemoved(diff.removed) - err = multierr.Append(err, l.processAdded(diff.added)) - err = multierr.Append(err, l.processUpdate(diff.updated)) + err = multierr.Append(err, l.processAdded(ctx, diff.added)) + err = multierr.Append(err, l.processUpdate(ctx, diff.updated)) return err } // processUpdate will manage when configurations of an existing don are updated // If new oracles are needed, they are created and started. Old ones will be shut down -func (l *launcher) processUpdate(updated map[registrysyncer.DonID]registrysyncer.DON) error { +func (l *launcher) processUpdate(ctx context.Context, updated map[registrysyncer.DonID]registrysyncer.DON) error { l.lock.Lock() defer l.lock.Unlock() @@ -203,12 +207,13 @@ func (l *launcher) processUpdate(updated map[registrysyncer.DonID]registrysyncer return fmt.Errorf("invariant violation: expected to find CCIP DON %d in the map of running deployments", don.ID) } - latestConfigs, err := getConfigsForDon(l.homeChainReader, don) + latestConfigs, err := getConfigsForDon(ctx, l.homeChainReader, don) if err != nil { return err } newPlugins, err := updateDON( + ctx, l.lggr, l.myP2PID, prevPlugins, @@ -233,16 +238,17 @@ func (l *launcher) processUpdate(updated map[registrysyncer.DonID]registrysyncer // processAdded is for when a new don is created. We know that all oracles // must be created and started -func (l *launcher) processAdded(added map[registrysyncer.DonID]registrysyncer.DON) error { +func (l *launcher) processAdded(ctx context.Context, added map[registrysyncer.DonID]registrysyncer.DON) error { l.lock.Lock() defer l.lock.Unlock() for donID, don := range added { - configs, err := getConfigsForDon(l.homeChainReader, don) + configs, err := getConfigsForDon(ctx, l.homeChainReader, don) if err != nil { return fmt.Errorf("failed to get current configs for don %d: %w", donID, err) } newPlugins, err := createDON( + ctx, l.lggr, l.myP2PID, don, @@ -300,6 +306,7 @@ func (l *launcher) processRemoved(removed map[registrysyncer.DonID]registrysynce } func updateDON( + ctx context.Context, lggr logger.Logger, p2pID ragep2ptypes.PeerID, prevPlugins pluginRegistry, @@ -318,7 +325,7 @@ func updateDON( for _, c := range latestConfigs { digest := c.ConfigDigest if _, ok := prevPlugins[digest]; !ok { - oracle, err := oracleCreator.Create(don.ID, cctypes.OCR3ConfigWithMeta(c)) + oracle, err := oracleCreator.Create(ctx, don.ID, cctypes.OCR3ConfigWithMeta(c)) if err != nil { return nil, fmt.Errorf("failed to create CCIP oracle: %w for digest %x", err, digest) } @@ -335,6 +342,7 @@ func updateDON( // createDON is a pure function that handles the case where a new DON is added to the capability registry. // It returns up to 4 plugins that are later started. func createDON( + ctx context.Context, lggr logger.Logger, p2pID ragep2ptypes.PeerID, don registrysyncer.DON, @@ -352,7 +360,7 @@ func createDON( return nil, fmt.Errorf("digest does not match type %w", err) } - oracle, err := oracleCreator.Create(don.ID, cctypes.OCR3ConfigWithMeta(config)) + oracle, err := oracleCreator.Create(ctx, don.ID, cctypes.OCR3ConfigWithMeta(config)) if err != nil { return nil, fmt.Errorf("failed to create CCIP oracle: %w for digest %x", err, digest) } @@ -363,16 +371,17 @@ func createDON( } func getConfigsForDon( + ctx context.Context, homeChainReader ccipreader.HomeChain, don registrysyncer.DON) ([]ccipreader.OCR3ConfigWithMeta, error) { // this should be a retryable error. - commitOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.ID, uint8(cctypes.PluginTypeCCIPCommit)) + commitOCRConfigs, err := homeChainReader.GetOCRConfigs(ctx, don.ID, uint8(cctypes.PluginTypeCCIPCommit)) if err != nil { return nil, fmt.Errorf("failed to fetch OCR configs for CCIP commit plugin (don id: %d) from home chain config contract: %w", don.ID, err) } - execOCRConfigs, err := homeChainReader.GetOCRConfigs(context.Background(), don.ID, uint8(cctypes.PluginTypeCCIPExec)) + execOCRConfigs, err := homeChainReader.GetOCRConfigs(ctx, don.ID, uint8(cctypes.PluginTypeCCIPExec)) if err != nil { return nil, fmt.Errorf("failed to fetch OCR configs for CCIP exec plugin (don id: %d) from home chain config contract: %w", don.ID, err) diff --git a/core/capabilities/ccip/launcher/launcher_test.go b/core/capabilities/ccip/launcher/launcher_test.go index 188ee48c215..3e3bd1a4368 100644 --- a/core/capabilities/ccip/launcher/launcher_test.go +++ b/core/capabilities/ccip/launcher/launcher_test.go @@ -8,6 +8,7 @@ import ( cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ragep2ptypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/stretchr/testify/mock" @@ -113,7 +114,7 @@ func Test_createDON(t *testing.T) { }, }, nil) oracleCreator.EXPECT().Type().Return(cctypes.OracleTypeBootstrap).Once() - oracleCreator.EXPECT().Create(mock.Anything, mock.Anything).Return(mocks.NewCCIPOracle(t), nil).Twice() + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.Anything).Return(mocks.NewCCIPOracle(t), nil).Twice() }, false, }, @@ -153,11 +154,11 @@ func Test_createDON(t *testing.T) { }, }, nil) - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) })). Return(mocks.NewCCIPOracle(t), nil) - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) })). Return(mocks.NewCCIPOracle(t), nil) @@ -212,11 +213,11 @@ func Test_createDON(t *testing.T) { }, }, nil) - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) })). Return(mocks.NewCCIPOracle(t), nil).Twice() - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) })). Return(mocks.NewCCIPOracle(t), nil).Twice() @@ -229,10 +230,11 @@ func Test_createDON(t *testing.T) { if tt.expect != nil { tt.expect(t, tt.args, tt.args.oracleCreator, tt.args.homeChainReader) } + ctx := testutils.Context(t) - latestConfigs, err := getConfigsForDon(tt.args.homeChainReader, tt.args.don) + latestConfigs, err := getConfigsForDon(ctx, tt.args.homeChainReader, tt.args.don) require.NoError(t, err) - _, err = createDON(tt.args.lggr, tt.args.p2pID, tt.args.don, tt.args.oracleCreator, latestConfigs) + _, err = createDON(ctx, tt.args.lggr, tt.args.p2pID, tt.args.don, tt.args.oracleCreator, latestConfigs) if tt.wantErr { require.Error(t, err) } else { @@ -304,11 +306,11 @@ func Test_updateDON(t *testing.T) { ConfigDigest: utils.RandomBytes32(), }, }, nil) - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) })). Return(mocks.NewCCIPOracle(t), nil) - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) })). Return(mocks.NewCCIPOracle(t), nil) @@ -405,11 +407,11 @@ func Test_updateDON(t *testing.T) { ConfigDigest: utils.RandomBytes32(), }, }, nil) - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) })). Return(mocks.NewCCIPOracle(t), nil).Once() - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) })). Return(mocks.NewCCIPOracle(t), nil).Once() @@ -472,11 +474,11 @@ func Test_updateDON(t *testing.T) { ConfigDigest: utils.RandomBytes32(), }, }, nil) - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) })). Return(mocks.NewCCIPOracle(t), nil).Twice() - oracleCreator.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + oracleCreator.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) })). Return(mocks.NewCCIPOracle(t), nil).Twice() @@ -489,10 +491,11 @@ func Test_updateDON(t *testing.T) { if tt.expect != nil { tt.expect(t, tt.args, tt.args.oracleCreator, tt.args.homeChainReader) } + ctx := testutils.Context(t) - latestConfigs, err := getConfigsForDon(tt.args.homeChainReader, tt.args.don) + latestConfigs, err := getConfigsForDon(ctx, tt.args.homeChainReader, tt.args.don) require.NoError(t, err) - newPlugins, err := updateDON(tt.args.lggr, tt.args.p2pID, tt.args.prevPlugins, tt.args.don, tt.args.oracleCreator, latestConfigs) + newPlugins, err := updateDON(ctx, tt.args.lggr, tt.args.p2pID, tt.args.prevPlugins, tt.args.don, tt.args.oracleCreator, latestConfigs) if (err != nil) != tt.wantErr { t.Errorf("updateDON() error = %v, wantErr %v", err, tt.wantErr) return @@ -602,11 +605,11 @@ func Test_launcher_processDiff(t *testing.T) { commitOracle.On("Start").Return(nil) execOracle := mocks.NewCCIPOracle(t) execOracle.On("Start").Return(nil) - m.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + m.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) })). Return(commitOracle, nil) - m.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + m.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) })). Return(execOracle, nil) @@ -679,11 +682,11 @@ func Test_launcher_processDiff(t *testing.T) { commitOracle.On("Start").Return(nil) execOracle := mocks.NewCCIPOracle(t) execOracle.On("Start").Return(nil) - m.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + m.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) })). Return(commitOracle, nil) - m.EXPECT().Create(mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { + m.EXPECT().Create(mock.Anything, mock.Anything, mock.MatchedBy(func(cfg cctypes.OCR3ConfigWithMeta) bool { return cfg.Config.PluginType == uint8(cctypes.PluginTypeCCIPExec) })). Return(execOracle, nil) @@ -733,7 +736,7 @@ func Test_launcher_processDiff(t *testing.T) { homeChainReader: tt.fields.homeChainReader, oracleCreator: tt.fields.oracleCreator, } - err := l.processDiff(tt.args.diff) + err := l.processDiff(testutils.Context(t), tt.args.diff) if tt.wantErr { require.Error(t, err) } else { diff --git a/core/capabilities/ccip/oraclecreator/bootstrap.go b/core/capabilities/ccip/oraclecreator/bootstrap.go index 44ed824e569..632ac789c8e 100644 --- a/core/capabilities/ccip/oraclecreator/bootstrap.go +++ b/core/capabilities/ccip/oraclecreator/bootstrap.go @@ -140,7 +140,7 @@ func (i *bootstrapOracleCreator) Type() cctypes.OracleType { } // Create implements types.OracleCreator. -func (i *bootstrapOracleCreator) Create(_ uint32, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { +func (i *bootstrapOracleCreator) Create(ctx context.Context, _ uint32, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { // Assuming that the chain selector is referring to an evm chain for now. // TODO: add an api that returns chain family. // NOTE: this doesn't really matter for the bootstrap node, it doesn't do anything on-chain. @@ -158,7 +158,6 @@ func (i *bootstrapOracleCreator) Create(_ uint32, config cctypes.OCR3ConfigWithM oraclePeerIDs = append(oraclePeerIDs, n.P2pID) } - ctx := context.Background() rmnHomeReader, err := i.getRmnHomeReader(ctx, config) if err != nil { return nil, fmt.Errorf("failed to get RMNHome reader: %w", err) diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index 2b4fae933a3..5df0b1135d7 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -118,7 +118,7 @@ func (i *pluginOracleCreator) Type() cctypes.OracleType { } // Create implements types.OracleCreator. -func (i *pluginOracleCreator) Create(donID uint32, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { +func (i *pluginOracleCreator) Create(ctx context.Context, donID uint32, config cctypes.OCR3ConfigWithMeta) (cctypes.CCIPOracle, error) { pluginType := cctypes.PluginType(config.Config.PluginType) // Assuming that the chain selector is referring to an evm chain for now. @@ -137,6 +137,7 @@ func (i *pluginOracleCreator) Create(donID uint32, config cctypes.OCR3ConfigWith } contractReaders, chainWriters, err := i.createReadersAndWriters( + ctx, destChainID, pluginType, config, @@ -294,6 +295,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( } func (i *pluginOracleCreator) createReadersAndWriters( + ctx context.Context, destChainID uint64, pluginType cctypes.PluginType, config cctypes.OCR3ConfigWithMeta, @@ -335,20 +337,19 @@ func (i *pluginOracleCreator) createReadersAndWriters( return nil, nil, fmt.Errorf("failed to get chain selector from chain ID %s: %w", chainID.String(), err1) } - chainReaderConfig, err1 := getChainReaderConfig(chainID.Uint64(), destChainID, homeChainID, ofc, chainSelector) + chainReaderConfig, err1 := getChainReaderConfig(i.lggr, chainID.Uint64(), destChainID, homeChainID, ofc, chainSelector) if err1 != nil { return nil, nil, fmt.Errorf("failed to get chain reader config: %w", err1) } - // TODO: context. - cr, err1 := relayer.NewContractReader(context.Background(), chainReaderConfig) + cr, err1 := relayer.NewContractReader(ctx, chainReaderConfig) if err1 != nil { return nil, nil, err1 } if chainID.Uint64() == destChainID { offrampAddressHex := common.BytesToAddress(config.Config.OfframpAddress).Hex() - err2 := cr.Bind(context.Background(), []types.BoundContract{ + err2 := cr.Bind(ctx, []types.BoundContract{ { Address: offrampAddressHex, Name: consts.ContractNameOffRamp, @@ -359,11 +360,12 @@ func (i *pluginOracleCreator) createReadersAndWriters( } } - if err2 := cr.Start(context.Background()); err2 != nil { + if err2 := cr.Start(ctx); err2 != nil { return nil, nil, fmt.Errorf("failed to start contract reader for chain %s: %w", chainID.String(), err2) } cw, err1 := createChainWriter( + ctx, chainID, i.evmConfigs, relayer, @@ -373,7 +375,7 @@ func (i *pluginOracleCreator) createReadersAndWriters( return nil, nil, err1 } - if err4 := cw.Start(context.Background()); err4 != nil { + if err4 := cw.Start(ctx); err4 != nil { return nil, nil, fmt.Errorf("failed to start chain writer for chain %s: %w", chainID.String(), err4) } @@ -430,6 +432,7 @@ func (i *pluginOracleCreator) getChainID(chainSelector cciptypes.ChainSelector) } func getChainReaderConfig( + lggr logger.Logger, chainID uint64, destChainID uint64, homeChainID uint64, @@ -444,14 +447,17 @@ func getChainReaderConfig( } if !ofc.commitEmpty() && ofc.commit().PriceFeedChainSelector == chainSelector { + lggr.Debugw("Adding feed reader config", "chainID", chainID) chainReaderConfig = evmconfig.MergeReaderConfigs(chainReaderConfig, evmconfig.FeedReaderConfig) } - if isUSDCEnabled(chainID, destChainID, ofc) { + if isUSDCEnabled(ofc) { + lggr.Debugw("Adding USDC reader config", "chainID", chainID) chainReaderConfig = evmconfig.MergeReaderConfigs(chainReaderConfig, evmconfig.USDCReaderConfig) } if chainID == homeChainID { + lggr.Debugw("Adding home chain reader config", "chainID", chainID) chainReaderConfig = evmconfig.MergeReaderConfigs(chainReaderConfig, evmconfig.HomeChainReaderConfigRaw) } @@ -463,11 +469,7 @@ func getChainReaderConfig( return marshaledConfig, nil } -func isUSDCEnabled(chainID uint64, destChainID uint64, ofc offChainConfig) bool { - if chainID == destChainID { - return false - } - +func isUSDCEnabled(ofc offChainConfig) bool { if ofc.execEmpty() { return false } @@ -476,6 +478,7 @@ func isUSDCEnabled(chainID uint64, destChainID uint64, ofc offChainConfig) bool } func createChainWriter( + ctx context.Context, chainID *big.Int, evmConfigs toml.EVMConfigs, relayer loop.Relayer, @@ -509,8 +512,7 @@ func createChainWriter( return nil, fmt.Errorf("failed to marshal chain writer config: %w", err) } - // TODO: context. - cw, err := relayer.NewChainWriter(context.Background(), chainWriterConfig) + cw, err := relayer.NewChainWriter(ctx, chainWriterConfig) if err != nil { return nil, fmt.Errorf("failed to create chain writer for chain %s: %w", chainID.String(), err) } diff --git a/core/capabilities/ccip/types/mocks/oracle_creator.go b/core/capabilities/ccip/types/mocks/oracle_creator.go index 51103c4a504..1906df7e063 100644 --- a/core/capabilities/ccip/types/mocks/oracle_creator.go +++ b/core/capabilities/ccip/types/mocks/oracle_creator.go @@ -3,6 +3,8 @@ package mocks import ( + context "context" + types "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" mock "github.com/stretchr/testify/mock" ) @@ -20,9 +22,9 @@ func (_m *OracleCreator) EXPECT() *OracleCreator_Expecter { return &OracleCreator_Expecter{mock: &_m.Mock} } -// Create provides a mock function with given fields: donID, config -func (_m *OracleCreator) Create(donID uint32, config types.OCR3ConfigWithMeta) (types.CCIPOracle, error) { - ret := _m.Called(donID, config) +// Create provides a mock function with given fields: ctx, donID, config +func (_m *OracleCreator) Create(ctx context.Context, donID uint32, config types.OCR3ConfigWithMeta) (types.CCIPOracle, error) { + ret := _m.Called(ctx, donID, config) if len(ret) == 0 { panic("no return value specified for Create") @@ -30,19 +32,19 @@ func (_m *OracleCreator) Create(donID uint32, config types.OCR3ConfigWithMeta) ( var r0 types.CCIPOracle var r1 error - if rf, ok := ret.Get(0).(func(uint32, types.OCR3ConfigWithMeta) (types.CCIPOracle, error)); ok { - return rf(donID, config) + if rf, ok := ret.Get(0).(func(context.Context, uint32, types.OCR3ConfigWithMeta) (types.CCIPOracle, error)); ok { + return rf(ctx, donID, config) } - if rf, ok := ret.Get(0).(func(uint32, types.OCR3ConfigWithMeta) types.CCIPOracle); ok { - r0 = rf(donID, config) + if rf, ok := ret.Get(0).(func(context.Context, uint32, types.OCR3ConfigWithMeta) types.CCIPOracle); ok { + r0 = rf(ctx, donID, config) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(types.CCIPOracle) } } - if rf, ok := ret.Get(1).(func(uint32, types.OCR3ConfigWithMeta) error); ok { - r1 = rf(donID, config) + if rf, ok := ret.Get(1).(func(context.Context, uint32, types.OCR3ConfigWithMeta) error); ok { + r1 = rf(ctx, donID, config) } else { r1 = ret.Error(1) } @@ -56,15 +58,16 @@ type OracleCreator_Create_Call struct { } // Create is a helper method to define mock.On call +// - ctx context.Context // - donID uint32 // - config types.OCR3ConfigWithMeta -func (_e *OracleCreator_Expecter) Create(donID interface{}, config interface{}) *OracleCreator_Create_Call { - return &OracleCreator_Create_Call{Call: _e.mock.On("Create", donID, config)} +func (_e *OracleCreator_Expecter) Create(ctx interface{}, donID interface{}, config interface{}) *OracleCreator_Create_Call { + return &OracleCreator_Create_Call{Call: _e.mock.On("Create", ctx, donID, config)} } -func (_c *OracleCreator_Create_Call) Run(run func(donID uint32, config types.OCR3ConfigWithMeta)) *OracleCreator_Create_Call { +func (_c *OracleCreator_Create_Call) Run(run func(ctx context.Context, donID uint32, config types.OCR3ConfigWithMeta)) *OracleCreator_Create_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(uint32), args[1].(types.OCR3ConfigWithMeta)) + run(args[0].(context.Context), args[1].(uint32), args[2].(types.OCR3ConfigWithMeta)) }) return _c } @@ -74,7 +77,7 @@ func (_c *OracleCreator_Create_Call) Return(_a0 types.CCIPOracle, _a1 error) *Or return _c } -func (_c *OracleCreator_Create_Call) RunAndReturn(run func(uint32, types.OCR3ConfigWithMeta) (types.CCIPOracle, error)) *OracleCreator_Create_Call { +func (_c *OracleCreator_Create_Call) RunAndReturn(run func(context.Context, uint32, types.OCR3ConfigWithMeta) (types.CCIPOracle, error)) *OracleCreator_Create_Call { _c.Call.Return(run) return _c } diff --git a/core/capabilities/ccip/types/types.go b/core/capabilities/ccip/types/types.go index 04da1157b33..8341adf2030 100644 --- a/core/capabilities/ccip/types/types.go +++ b/core/capabilities/ccip/types/types.go @@ -1,6 +1,8 @@ package types import ( + "context" + ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" ) @@ -46,7 +48,7 @@ type OracleCreator interface { // Create creates a new oracle that will run either the commit or exec ccip plugin, // if its a plugin oracle, or a bootstrap oracle if its a bootstrap oracle. // The oracle must be returned unstarted. - Create(donID uint32, config OCR3ConfigWithMeta) (CCIPOracle, error) + Create(ctx context.Context, donID uint32, config OCR3ConfigWithMeta) (CCIPOracle, error) // Type returns the type of oracle that this creator creates. // The only valid values are OracleTypePlugin and OracleTypeBootstrap. diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index 1f5275c13fb..b6cb9adb875 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -65,13 +65,13 @@ func NewBalanceMonitor(ethClient evmclient.Client, ethKeyStore keystore.Eth, lgg Start: bm.start, Close: bm.close, }.NewServiceEngine(lggr) - bm.sleeperTask = utils.NewSleeperTask(&worker{bm: bm}) + bm.sleeperTask = utils.NewSleeperTaskCtx(&worker{bm: bm}) return bm } func (bm *balanceMonitor) start(ctx context.Context) error { // Always query latest balance on start - (&worker{bm}).WorkCtx(ctx) + (&worker{bm}).Work(ctx) return nil } @@ -146,12 +146,7 @@ func (*worker) Name() string { return "BalanceMonitorWorker" } -func (w *worker) Work() { - // Used with SleeperTask - w.WorkCtx(context.Background()) -} - -func (w *worker) WorkCtx(ctx context.Context) { +func (w *worker) Work(ctx context.Context) { enabledAddresses, err := w.bm.ethKeyStore.EnabledAddressesForChain(ctx, w.bm.chainID) if err != nil { w.bm.eng.Error("BalanceMonitor: error getting keys", err) diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index 3fca9d6ad09..689e7d27d26 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -474,6 +474,13 @@ func (s *Shell) runNode(c *cli.Context) error { } } + if s.Config.Capabilities().Peering().Enabled() { + err2 := app.GetKeyStore().Workflow().EnsureKey(rootCtx) + if err2 != nil { + return errors.Wrap(err2, "failed to ensure workflow key") + } + } + err2 := app.GetKeyStore().CSA().EnsureKey(rootCtx) if err2 != nil { return errors.Wrap(err2, "failed to ensure CSA key") diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index b67c57ba40e..62360cb02cb 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -336,8 +336,8 @@ CacheTimeout = '10s' # Default [EVM.HeadTracker] # HistoryDepth tracks the top N blocks on top of the latest finalized block to keep in the `heads` database table. # Note that this can easily result in MORE than `N + finality depth` records since in the case of re-orgs we keep multiple heads for a particular block height. -# This number should be at least as large as `FinalityDepth`. -# There may be a small performance penalty to setting this to something very large (10,000+) +# Higher values help reduce number of RPC requests performed by TXM's Finalizer and improve TXM's Confirmer reorg protection on restarts. +# At the same time, setting the value too high could lead to higher CPU consumption. The following formula could be used to calculate the optimal value: `expected_downtime_on_restart/block_time`. HistoryDepth = 100 # Default # MaxBufferSize is the maximum number of heads that may be # buffered in front of the head tracker before older heads start to be diff --git a/core/config/docs/chains-solana.toml b/core/config/docs/chains-solana.toml index 626c2f0613f..87d71b49cc6 100644 --- a/core/config/docs/chains-solana.toml +++ b/core/config/docs/chains-solana.toml @@ -36,6 +36,11 @@ ComputeUnitPriceDefault = 0 # Default FeeBumpPeriod = '3s' # Default # BlockHistoryPollPeriod is the rate to poll for blocks in the block history fee estimator BlockHistoryPollPeriod = '5s' # Default +# BlockHistorySize is the number of blocks to take into consideration when using FeeEstimatorMode = 'blockhistory' to determine compute unit price. +# If set to 1, the compute unit price will be determined by the median of the last block's compute unit prices. +# If set N > 1, the compute unit price will be determined by the average of the medians of the last N blocks' compute unit prices. +# DISCLAIMER: 1:1 ratio between n and RPC calls. It executes once every 'BlockHistoryPollPeriod' value. +BlockHistorySize = 1 # Default # ComputeUnitLimitDefault is the compute units limit applied to transactions unless overriden during the txm enqueue ComputeUnitLimitDefault = 200_000 # Default # EstimateComputeUnitLimit enables or disables compute unit limit estimations per transaction. If estimations return 0 used compute, the ComputeUnitLimitDefault value is used, if set. diff --git a/core/gethwrappers/ccip/generated/burn_from_mint_token_pool/burn_from_mint_token_pool.go b/core/gethwrappers/ccip/generated/burn_from_mint_token_pool/burn_from_mint_token_pool.go index 17da4f13be6..ff7ccd3a5dd 100644 --- a/core/gethwrappers/ccip/generated/burn_from_mint_token_pool/burn_from_mint_token_pool.go +++ b/core/gethwrappers/ccip/generated/burn_from_mint_token_pool/burn_from_mint_token_pool.go @@ -82,8 +82,8 @@ type TokenPoolChainUpdate struct { } var BurnFromMintTokenPoolMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractIBurnMintERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60e06040523480156200001157600080fd5b50604051620044183803806200441883398101604081905262000034916200085d565b83838383336000816200005a57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008d576200008d8162000159565b50506001600160a01b0384161580620000ad57506001600160a01b038116155b80620000c057506001600160a01b038216155b15620000df576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013257604080516000815260208101909152620001329084620001d3565b506200014f925050506001600160a01b0385163060001962000330565b5050505062000a99565b336001600160a01b038216036200018357604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001f4576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200027f5760008382815181106200021857620002186200096d565b602090810291909101015190506200023260028262000416565b1562000275576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001f7565b5060005b81518110156200032b576000828281518110620002a457620002a46200096d565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002d0575062000322565b620002dd60028262000436565b1562000320576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000283565b505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801562000382573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003a8919062000983565b620003b49190620009b3565b604080516001600160a01b038616602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915291925062000410918691906200044d16565b50505050565b60006200042d836001600160a01b03841662000522565b90505b92915050565b60006200042d836001600160a01b03841662000626565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200049c906001600160a01b03851690849062000678565b8051909150156200032b5780806020019051810190620004bd9190620009c9565b6200032b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b600081815260018301602052604081205480156200061b57600062000549600183620009f4565b85549091506000906200055f90600190620009f4565b9050808214620005cb5760008660000182815481106200058357620005836200096d565b9060005260206000200154905080876000018481548110620005a957620005a96200096d565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080620005df57620005df62000a0a565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000430565b600091505062000430565b60008181526001830160205260408120546200066f5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000430565b50600062000430565b606062000689848460008562000691565b949350505050565b606082471015620006f45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000519565b600080866001600160a01b0316858760405162000712919062000a46565b60006040518083038185875af1925050503d806000811462000751576040519150601f19603f3d011682016040523d82523d6000602084013e62000756565b606091505b5090925090506200076a8783838762000775565b979650505050505050565b60608315620007e9578251600003620007e1576001600160a01b0385163b620007e15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000519565b508162000689565b620006898383815115620008005781518083602001fd5b8060405162461bcd60e51b815260040162000519919062000a64565b6001600160a01b03811681146200083257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b805162000858816200081c565b919050565b600080600080608085870312156200087457600080fd5b845162000881816200081c565b602086810151919550906001600160401b0380821115620008a157600080fd5b818801915088601f830112620008b657600080fd5b815181811115620008cb57620008cb62000835565b8060051b604051601f19603f83011681018181108582111715620008f357620008f362000835565b60405291825284820192508381018501918b8311156200091257600080fd5b938501935b828510156200093b576200092b856200084b565b8452938501939285019262000917565b80985050505050505062000952604086016200084b565b915062000962606086016200084b565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156200099657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808201808211156200043057620004306200099d565b600060208284031215620009dc57600080fd5b81518015158114620009ed57600080fd5b9392505050565b818103818111156200043057620004306200099d565b634e487b7160e01b600052603160045260246000fd5b60005b8381101562000a3d57818101518382015260200162000a23565b50506000910152565b6000825162000a5a81846020870162000a20565b9190910192915050565b602081526000825180602084015262000a8581604085016020870162000a20565b601f01601f19169190910160400192915050565b60805160a05160c05161390262000b16600039600081816104dd0152818161171801526120cb0152600081816104b70152818161157901526119ce0152600081816102390152818161028e015281816106e001528181611499015281816118ee01528181611ae60152818161206101526122b601526139026000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80639a4575b9116100ee578063c4bffe2b11610097578063db6327dc11610071578063db6327dc146104a2578063dc0bd971146104b5578063e0351e13146104db578063f2fde38b1461050157600080fd5b8063c4bffe2b14610467578063c75eea9c1461047c578063cf7401f31461048f57600080fd5b8063b0f479a1116100c8578063b0f479a114610423578063b794658014610441578063c0d786551461045457600080fd5b80639a4575b91461037f578063a7cd63b71461039f578063af58d59f146103b457600080fd5b806354c8a4f31161015b57806379ba50971161013557806379ba5097146103335780637d54534e1461033b5780638926f54f1461034e5780638da5cb5b1461036157600080fd5b806354c8a4f3146102ed5780636d3d1a581461030257806378a010b21461032057600080fd5b806321df0da71161018c57806321df0da714610237578063240028e81461027e57806339077537146102cb57600080fd5b806301ffc9a7146101b35780630a2fd493146101db578063181f5a77146101fb575b600080fd5b6101c66101c1366004612a59565b610514565b60405190151581526020015b60405180910390f35b6101ee6101e9366004612ab8565b6105f9565b6040516101d29190612b37565b6101ee6040518060400160405280601b81526020017f4275726e46726f6d4d696e74546f6b656e506f6f6c20312e352e30000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b6101c661028c366004612b77565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b6102de6102d9366004612b94565b6106a9565b604051905181526020016101d2565b6103006102fb366004612c1c565b61082f565b005b60085473ffffffffffffffffffffffffffffffffffffffff16610259565b61030061032e366004612c88565b6108aa565b610300610a1e565b610300610349366004612b77565b610aec565b6101c661035c366004612ab8565b610b3b565b60015473ffffffffffffffffffffffffffffffffffffffff16610259565b61039261038d366004612d0b565b610b52565b6040516101d29190612d46565b6103a7610bf9565b6040516101d29190612da6565b6103c76103c2366004612ab8565b610c0a565b6040516101d2919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff16610259565b6101ee61044f366004612ab8565b610cdf565b610300610462366004612b77565b610d0a565b61046f610de5565b6040516101d29190612e00565b6103c761048a366004612ab8565b610e9d565b61030061049d366004612f68565b610f6f565b6103006104b0366004612fad565b610ff8565b7f0000000000000000000000000000000000000000000000000000000000000000610259565b7f00000000000000000000000000000000000000000000000000000000000000006101c6565b61030061050f366004612b77565b61147e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806105a757507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806105f357507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061062490612fef565b80601f016020809104026020016040519081016040528092919081815260200182805461065090612fef565b801561069d5780601f106106725761010080835404028352916020019161069d565b820191906000526020600020905b81548152906001019060200180831161068057829003601f168201915b50505050509050919050565b6040805160208101909152600081526106c96106c4836130ed565b611492565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166340c10f196107156060850160408601612b77565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260608501356024820152604401600060405180830381600087803b15801561078557600080fd5b505af1158015610799573d6000803e3d6000fd5b506107ae925050506060830160408401612b77565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0846060013560405161081091815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6108376116c3565b6108a48484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061171692505050565b50505050565b6108b26116c3565b6108bb83610b3b565b610902576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b67ffffffffffffffff83166000908152600760205260408120600401805461092990612fef565b80601f016020809104026020016040519081016040528092919081815260200182805461095590612fef565b80156109a25780601f10610977576101008083540402835291602001916109a2565b820191906000526020600020905b81548152906001019060200180831161098557829003601f168201915b5050505067ffffffffffffffff86166000908152600760205260409020919250506004016109d1838583613232565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610a109392919061334c565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a6f576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610af46116c3565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60006105f3600567ffffffffffffffff84166118cc565b6040805180820190915260608082526020820152610b77610b72836133b0565b6118e7565b610b848260600135611ab1565b6040516060830135815233907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a26040518060400160405280610bde84602001602081019061044f9190612ab8565b81526040805160208181019092526000815291015292915050565b6060610c056002611b5a565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526105f390611b67565b67ffffffffffffffff8116600090815260076020526040902060050180546060919061062490612fef565b610d126116c3565b73ffffffffffffffffffffffffffffffffffffffff8116610d5f576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b60606000610df36005611b5a565b90506000815167ffffffffffffffff811115610e1157610e11612e42565b604051908082528060200260200182016040528015610e3a578160200160208202803683370190505b50905060005b8251811015610e9657828181518110610e5b57610e5b613452565b6020026020010151828281518110610e7557610e75613452565b67ffffffffffffffff90921660209283029190910190910152600101610e40565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526105f390611b67565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590610faf575060015473ffffffffffffffffffffffffffffffffffffffff163314155b15610fe8576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b610ff3838383611c19565b505050565b6110006116c3565b60005b81811015610ff357600083838381811061101f5761101f613452565b90506020028101906110319190613481565b61103a906134bf565b905061104f8160800151826020015115611d03565b6110628160a00151826020015115611d03565b80602001511561135e5780516110849060059067ffffffffffffffff16611e3c565b6110c95780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b60408101515115806110de5750606081015151155b15611115576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906112f69082613573565b506060820151600582019061130b9082613573565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c29550611351949392919061368d565b60405180910390a1611475565b80516113769060059067ffffffffffffffff16611e48565b6113bb5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906114246004830182612a0b565b611432600583016000612a0b565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611003565b6114866116c3565b61148f81611e54565b50565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146115275760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa1580156115d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f99190613726565b15611630576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d8160200151611f18565b600061164c82602001516105f9565b9050805160001480611670575080805190602001208260a001518051906020012014155b156116ad578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016108f99190612b37565b6116bf8260200151836060015161203e565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611714576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f000000000000000000000000000000000000000000000000000000000000000061176d576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b825181101561180357600083828151811061178d5761178d613452565b602002602001015190506117ab81600261208590919063ffffffff16565b156117fa5760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611770565b5060005b8151811015610ff357600082828151811061182457611824613452565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361186857506118c4565b6118736002826120a7565b156118c25760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611807565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161461197c5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4e9190613726565b15611a85576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a9281604001516120c9565b611a9f8160200151612148565b61148f81602001518260600151612296565b6040517f79cc6790000000000000000000000000000000000000000000000000000000008152306004820152602481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906379cc679090604401600060405180830381600087803b158015611b3f57600080fd5b505af1158015611b53573d6000803e3d6000fd5b5050505050565b606060006118e0836122da565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611bf582606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611bd99190613772565b85608001516fffffffffffffffffffffffffffffffff16612335565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b611c2283610b3b565b611c64576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108f9565b611c6f826000611d03565b67ffffffffffffffff83166000908152600760205260409020611c92908361235f565b611c9d816000611d03565b67ffffffffffffffff83166000908152600760205260409020611cc3906002018261235f565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b838383604051611cf693929190613785565b60405180910390a1505050565b815115611dca5781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580611d59575060408201516fffffffffffffffffffffffffffffffff16155b15611d9257816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016108f99190613808565b80156116bf576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff16151580611e03575060208201516fffffffffffffffffffffffffffffffff1615155b156116bf57816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016108f99190613808565b60006118e08383612501565b60006118e08383612550565b3373ffffffffffffffffffffffffffffffffffffffff821603611ea3576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611f2181610b3b565b611f63576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015611fe2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120069190613726565b61148f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116bf90600201827f0000000000000000000000000000000000000000000000000000000000000000612643565b60006118e08373ffffffffffffffffffffffffffffffffffffffff8416612550565b60006118e08373ffffffffffffffffffffffffffffffffffffffff8416612501565b7f00000000000000000000000000000000000000000000000000000000000000001561148f576120fa6002826129c6565b61148f576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108f9565b61215181610b3b565b612193576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa15801561220c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122309190613844565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461148f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116bf90827f0000000000000000000000000000000000000000000000000000000000000000612643565b60608160000180548060200260200160405190810160405280929190818152602001828054801561069d57602002820191906000526020600020905b8154815260200190600101908083116123165750505050509050919050565b6000612354856123458486613861565b61234f9087613878565b6129f5565b90505b949350505050565b815460009061238890700100000000000000000000000000000000900463ffffffff1642613772565b9050801561242a57600183015483546123d0916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612335565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612450916fffffffffffffffffffffffffffffffff90811691166129f5565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990611cf6908490613808565b6000818152600183016020526040812054612548575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105f3565b5060006105f3565b60008181526001830160205260408120548015612639576000612574600183613772565b855490915060009061258890600190613772565b90508082146125ed5760008660000182815481106125a8576125a8613452565b90600052602060002001549050808760000184815481106125cb576125cb613452565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806125fe576125fe61388b565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105f3565b60009150506105f3565b825474010000000000000000000000000000000000000000900460ff16158061266a575081155b1561267457505050565b825460018401546fffffffffffffffffffffffffffffffff808316929116906000906126ba90700100000000000000000000000000000000900463ffffffff1642613772565b9050801561277a57818311156126fc576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546127369083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612335565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b848210156128315773ffffffffffffffffffffffffffffffffffffffff84166127d9576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016108f9565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016108f9565b848310156129445760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169060009082906128759082613772565b61287f878a613772565b6128899190613878565b61289391906138ba565b905073ffffffffffffffffffffffffffffffffffffffff86166128ec576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016108f9565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016108f9565b61294e8584613772565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415156118e0565b6000818310612a0457816118e0565b5090919050565b508054612a1790612fef565b6000825580601f10612a27575050565b601f01602090049060005260206000209081019061148f91905b80821115612a555760008155600101612a41565b5090565b600060208284031215612a6b57600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146118e057600080fd5b803567ffffffffffffffff81168114612ab357600080fd5b919050565b600060208284031215612aca57600080fd5b6118e082612a9b565b6000815180845260005b81811015612af957602081850181015186830182015201612add565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006118e06020830184612ad3565b73ffffffffffffffffffffffffffffffffffffffff8116811461148f57600080fd5b8035612ab381612b4a565b600060208284031215612b8957600080fd5b81356118e081612b4a565b600060208284031215612ba657600080fd5b813567ffffffffffffffff811115612bbd57600080fd5b820161010081850312156118e057600080fd5b60008083601f840112612be257600080fd5b50813567ffffffffffffffff811115612bfa57600080fd5b6020830191508360208260051b8501011115612c1557600080fd5b9250929050565b60008060008060408587031215612c3257600080fd5b843567ffffffffffffffff80821115612c4a57600080fd5b612c5688838901612bd0565b90965094506020870135915080821115612c6f57600080fd5b50612c7c87828801612bd0565b95989497509550505050565b600080600060408486031215612c9d57600080fd5b612ca684612a9b565b9250602084013567ffffffffffffffff80821115612cc357600080fd5b818601915086601f830112612cd757600080fd5b813581811115612ce657600080fd5b876020828501011115612cf857600080fd5b6020830194508093505050509250925092565b600060208284031215612d1d57600080fd5b813567ffffffffffffffff811115612d3457600080fd5b820160a081850312156118e057600080fd5b602081526000825160406020840152612d626060840182612ad3565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152612d9d8282612ad3565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015612df457835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612dc2565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612df457835167ffffffffffffffff1683529284019291840191600101612e1c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612e9557612e95612e42565b60405290565b60405160c0810167ffffffffffffffff81118282101715612e9557612e95612e42565b801515811461148f57600080fd5b8035612ab381612ebe565b80356fffffffffffffffffffffffffffffffff81168114612ab357600080fd5b600060608284031215612f0957600080fd5b6040516060810181811067ffffffffffffffff82111715612f2c57612f2c612e42565b6040529050808235612f3d81612ebe565b8152612f4b60208401612ed7565b6020820152612f5c60408401612ed7565b60408201525092915050565b600080600060e08486031215612f7d57600080fd5b612f8684612a9b565b9250612f958560208601612ef7565b9150612fa48560808601612ef7565b90509250925092565b60008060208385031215612fc057600080fd5b823567ffffffffffffffff811115612fd757600080fd5b612fe385828601612bd0565b90969095509350505050565b600181811c9082168061300357607f821691505b60208210810361303c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261305357600080fd5b813567ffffffffffffffff8082111561306e5761306e612e42565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156130b4576130b4612e42565b816040528381528660208588010111156130cd57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000610100823603121561310057600080fd5b613108612e71565b823567ffffffffffffffff8082111561312057600080fd5b61312c36838701613042565b835261313a60208601612a9b565b602084015261314b60408601612b6c565b60408401526060850135606084015261316660808601612b6c565b608084015260a085013591508082111561317f57600080fd5b61318b36838701613042565b60a084015260c08501359150808211156131a457600080fd5b6131b036838701613042565b60c084015260e08501359150808211156131c957600080fd5b506131d636828601613042565b60e08301525092915050565b601f821115610ff3576000816000526020600020601f850160051c8101602086101561320b5750805b601f850160051c820191505b8181101561322a57828155600101613217565b505050505050565b67ffffffffffffffff83111561324a5761324a612e42565b61325e836132588354612fef565b836131e2565b6000601f8411600181146132b0576000851561327a5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b53565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156132ff57868501358255602094850194600190920191016132df565b508682101561333a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b60408152600061335f6040830186612ad3565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a082360312156133c257600080fd5b60405160a0810167ffffffffffffffff82821081831117156133e6576133e6612e42565b8160405284359150808211156133fb57600080fd5b5061340836828601613042565b82525061341760208401612a9b565b6020820152604083013561342a81612b4a565b604082015260608381013590820152608083013561344781612b4a565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec18336030181126134b557600080fd5b9190910192915050565b600061014082360312156134d257600080fd5b6134da612e9b565b6134e383612a9b565b81526134f160208401612ecc565b6020820152604083013567ffffffffffffffff8082111561351157600080fd5b61351d36838701613042565b6040840152606085013591508082111561353657600080fd5b5061354336828601613042565b6060830152506135563660808501612ef7565b60808201526135683660e08501612ef7565b60a082015292915050565b815167ffffffffffffffff81111561358d5761358d612e42565b6135a18161359b8454612fef565b846131e2565b602080601f8311600181146135f457600084156135be5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561322a565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561364157888601518255948401946001909101908401613622565b508582101561367d57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff871683528060208401526136b181840187612ad3565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506136ef9050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152612d9d565b60006020828403121561373857600080fd5b81516118e081612ebe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f3576105f3613743565b67ffffffffffffffff8416815260e081016137d160208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612357565b606081016105f382848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561385657600080fd5b81516118e081612b4a565b80820281158282048414176105f3576105f3613743565b808201808211156105f3576105f3613743565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000826138f0577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"contractIBurnMintERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"RateLimitAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60e06040523480156200001157600080fd5b506040516200444a3803806200444a83398101604081905262000034916200085d565b83838383336000816200005a57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008d576200008d8162000159565b50506001600160a01b0384161580620000ad57506001600160a01b038116155b80620000c057506001600160a01b038216155b15620000df576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013257604080516000815260208101909152620001329084620001d3565b506200014f925050506001600160a01b0385163060001962000330565b5050505062000a99565b336001600160a01b038216036200018357604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001f4576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200027f5760008382815181106200021857620002186200096d565b602090810291909101015190506200023260028262000416565b1562000275576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001f7565b5060005b81518110156200032b576000828281518110620002a457620002a46200096d565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002d0575062000322565b620002dd60028262000436565b1562000320576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000283565b505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801562000382573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003a8919062000983565b620003b49190620009b3565b604080516001600160a01b038616602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915291925062000410918691906200044d16565b50505050565b60006200042d836001600160a01b03841662000522565b90505b92915050565b60006200042d836001600160a01b03841662000626565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200049c906001600160a01b03851690849062000678565b8051909150156200032b5780806020019051810190620004bd9190620009c9565b6200032b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b600081815260018301602052604081205480156200061b57600062000549600183620009f4565b85549091506000906200055f90600190620009f4565b9050808214620005cb5760008660000182815481106200058357620005836200096d565b9060005260206000200154905080876000018481548110620005a957620005a96200096d565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080620005df57620005df62000a0a565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000430565b600091505062000430565b60008181526001830160205260408120546200066f5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000430565b50600062000430565b606062000689848460008562000691565b949350505050565b606082471015620006f45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000519565b600080866001600160a01b0316858760405162000712919062000a46565b60006040518083038185875af1925050503d806000811462000751576040519150601f19603f3d011682016040523d82523d6000602084013e62000756565b606091505b5090925090506200076a8783838762000775565b979650505050505050565b60608315620007e9578251600003620007e1576001600160a01b0385163b620007e15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000519565b508162000689565b620006898383815115620008005781518083602001fd5b8060405162461bcd60e51b815260040162000519919062000a64565b6001600160a01b03811681146200083257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b805162000858816200081c565b919050565b600080600080608085870312156200087457600080fd5b845162000881816200081c565b602086810151919550906001600160401b0380821115620008a157600080fd5b818801915088601f830112620008b657600080fd5b815181811115620008cb57620008cb62000835565b8060051b604051601f19603f83011681018181108582111715620008f357620008f362000835565b60405291825284820192508381018501918b8311156200091257600080fd5b938501935b828510156200093b576200092b856200084b565b8452938501939285019262000917565b80985050505050505062000952604086016200084b565b915062000962606086016200084b565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156200099657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808201808211156200043057620004306200099d565b600060208284031215620009dc57600080fd5b81518015158114620009ed57600080fd5b9392505050565b818103818111156200043057620004306200099d565b634e487b7160e01b600052603160045260246000fd5b60005b8381101562000a3d57818101518382015260200162000a23565b50506000910152565b6000825162000a5a81846020870162000a20565b9190910192915050565b602081526000825180602084015262000a8581604085016020870162000a20565b601f01601f19169190910160400192915050565b60805160a05160c05161393462000b16600039600081816104dd0152818161174a01526120fd0152600081816104b7015281816115ab0152611a000152600081816102390152818161028e015281816106e0015281816114cb0152818161192001528181611b180152818161209301526122e801526139346000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80639a4575b9116100ee578063c4bffe2b11610097578063db6327dc11610071578063db6327dc146104a2578063dc0bd971146104b5578063e0351e13146104db578063f2fde38b1461050157600080fd5b8063c4bffe2b14610467578063c75eea9c1461047c578063cf7401f31461048f57600080fd5b8063b0f479a1116100c8578063b0f479a114610423578063b794658014610441578063c0d786551461045457600080fd5b80639a4575b91461037f578063a7cd63b71461039f578063af58d59f146103b457600080fd5b806354c8a4f31161015b57806379ba50971161013557806379ba5097146103335780637d54534e1461033b5780638926f54f1461034e5780638da5cb5b1461036157600080fd5b806354c8a4f3146102ed5780636d3d1a581461030257806378a010b21461032057600080fd5b806321df0da71161018c57806321df0da714610237578063240028e81461027e57806339077537146102cb57600080fd5b806301ffc9a7146101b35780630a2fd493146101db578063181f5a77146101fb575b600080fd5b6101c66101c1366004612a8b565b610514565b60405190151581526020015b60405180910390f35b6101ee6101e9366004612aea565b6105f9565b6040516101d29190612b69565b6101ee6040518060400160405280601b81526020017f4275726e46726f6d4d696e74546f6b656e506f6f6c20312e352e30000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b6101c661028c366004612ba9565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b6102de6102d9366004612bc6565b6106a9565b604051905181526020016101d2565b6103006102fb366004612c4e565b61082f565b005b60085473ffffffffffffffffffffffffffffffffffffffff16610259565b61030061032e366004612cba565b6108aa565b610300610a1e565b610300610349366004612ba9565b610aec565b6101c661035c366004612aea565b610b6d565b60015473ffffffffffffffffffffffffffffffffffffffff16610259565b61039261038d366004612d3d565b610b84565b6040516101d29190612d78565b6103a7610c2b565b6040516101d29190612dd8565b6103c76103c2366004612aea565b610c3c565b6040516101d2919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff16610259565b6101ee61044f366004612aea565b610d11565b610300610462366004612ba9565b610d3c565b61046f610e17565b6040516101d29190612e32565b6103c761048a366004612aea565b610ecf565b61030061049d366004612f9a565b610fa1565b6103006104b0366004612fdf565b61102a565b7f0000000000000000000000000000000000000000000000000000000000000000610259565b7f00000000000000000000000000000000000000000000000000000000000000006101c6565b61030061050f366004612ba9565b6114b0565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806105a757507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806105f357507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061062490613021565b80601f016020809104026020016040519081016040528092919081815260200182805461065090613021565b801561069d5780601f106106725761010080835404028352916020019161069d565b820191906000526020600020905b81548152906001019060200180831161068057829003601f168201915b50505050509050919050565b6040805160208101909152600081526106c96106c48361311f565b6114c4565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166340c10f196107156060850160408601612ba9565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260608501356024820152604401600060405180830381600087803b15801561078557600080fd5b505af1158015610799573d6000803e3d6000fd5b506107ae925050506060830160408401612ba9565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0846060013560405161081091815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6108376116f5565b6108a48484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061174892505050565b50505050565b6108b26116f5565b6108bb83610b6d565b610902576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b67ffffffffffffffff83166000908152600760205260408120600401805461092990613021565b80601f016020809104026020016040519081016040528092919081815260200182805461095590613021565b80156109a25780601f10610977576101008083540402835291602001916109a2565b820191906000526020600020905b81548152906001019060200180831161098557829003601f168201915b5050505067ffffffffffffffff86166000908152600760205260409020919250506004016109d1838583613264565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610a109392919061337e565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a6f576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610af46116f5565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d091749060200160405180910390a150565b60006105f3600567ffffffffffffffff84166118fe565b6040805180820190915260608082526020820152610ba9610ba4836133e2565b611919565b610bb68260600135611ae3565b6040516060830135815233907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a26040518060400160405280610c1084602001602081019061044f9190612aea565b81526040805160208181019092526000815291015292915050565b6060610c376002611b8c565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526105f390611b99565b67ffffffffffffffff8116600090815260076020526040902060050180546060919061062490613021565b610d446116f5565b73ffffffffffffffffffffffffffffffffffffffff8116610d91576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b60606000610e256005611b8c565b90506000815167ffffffffffffffff811115610e4357610e43612e74565b604051908082528060200260200182016040528015610e6c578160200160208202803683370190505b50905060005b8251811015610ec857828181518110610e8d57610e8d613484565b6020026020010151828281518110610ea757610ea7613484565b67ffffffffffffffff90921660209283029190910190910152600101610e72565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526105f390611b99565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590610fe1575060015473ffffffffffffffffffffffffffffffffffffffff163314155b1561101a576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b611025838383611c4b565b505050565b6110326116f5565b60005b8181101561102557600083838381811061105157611051613484565b905060200281019061106391906134b3565b61106c906134f1565b90506110818160800151826020015115611d35565b6110948160a00151826020015115611d35565b8060200151156113905780516110b69060059067ffffffffffffffff16611e6e565b6110fb5780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b60408101515115806111105750606081015151155b15611147576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c1790911696151502959095179098559081015194015193811693169091029190911760038201559151909190600482019061132890826135a5565b506060820151600582019061133d90826135a5565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c2955061138394939291906136bf565b60405180910390a16114a7565b80516113a89060059067ffffffffffffffff16611e7a565b6113ed5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906114566004830182612a3d565b611464600583016000612a3d565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611035565b6114b86116f5565b6114c181611e86565b50565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146115595760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611607573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162b9190613758565b15611662576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61166f8160200151611f4a565b600061167e82602001516105f9565b90508051600014806116a2575080805190602001208260a001518051906020012014155b156116df578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016108f99190612b69565b6116f182602001518360600151612070565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611746576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f000000000000000000000000000000000000000000000000000000000000000061179f576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82518110156118355760008382815181106117bf576117bf613484565b602002602001015190506117dd8160026120b790919063ffffffff16565b1561182c5760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b506001016117a2565b5060005b815181101561102557600082828151811061185657611856613484565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361189a57506118f6565b6118a56002826120d9565b156118f45760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611839565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146119ae5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611a5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a809190613758565b15611ab7576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ac481604001516120fb565b611ad1816020015161217a565b6114c1816020015182606001516122c8565b6040517f79cc6790000000000000000000000000000000000000000000000000000000008152306004820152602481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906379cc679090604401600060405180830381600087803b158015611b7157600080fd5b505af1158015611b85573d6000803e3d6000fd5b5050505050565b606060006119128361230c565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611c2782606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611c0b91906137a4565b85608001516fffffffffffffffffffffffffffffffff16612367565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b611c5483610b6d565b611c96576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108f9565b611ca1826000611d35565b67ffffffffffffffff83166000908152600760205260409020611cc49083612391565b611ccf816000611d35565b67ffffffffffffffff83166000908152600760205260409020611cf59060020182612391565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b838383604051611d28939291906137b7565b60405180910390a1505050565b815115611dfc5781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580611d8b575060408201516fffffffffffffffffffffffffffffffff16155b15611dc457816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016108f9919061383a565b80156116f1576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff16151580611e35575060208201516fffffffffffffffffffffffffffffffff1615155b156116f157816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016108f9919061383a565b60006119128383612533565b60006119128383612582565b3373ffffffffffffffffffffffffffffffffffffffff821603611ed5576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611f5381610b6d565b611f95576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015612014573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120389190613758565b6114c1576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116f190600201827f0000000000000000000000000000000000000000000000000000000000000000612675565b60006119128373ffffffffffffffffffffffffffffffffffffffff8416612582565b60006119128373ffffffffffffffffffffffffffffffffffffffff8416612533565b7f0000000000000000000000000000000000000000000000000000000000000000156114c15761212c6002826129f8565b6114c1576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108f9565b61218381610b6d565b6121c5576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa15801561223e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122629190613876565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146114c1576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116f190827f0000000000000000000000000000000000000000000000000000000000000000612675565b60608160000180548060200260200160405190810160405280929190818152602001828054801561069d57602002820191906000526020600020905b8154815260200190600101908083116123485750505050509050919050565b6000612386856123778486613893565b61238190876138aa565b612a27565b90505b949350505050565b81546000906123ba90700100000000000000000000000000000000900463ffffffff16426137a4565b9050801561245c5760018301548354612402916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612367565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612482916fffffffffffffffffffffffffffffffff9081169116612a27565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990611d2890849061383a565b600081815260018301602052604081205461257a575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105f3565b5060006105f3565b6000818152600183016020526040812054801561266b5760006125a66001836137a4565b85549091506000906125ba906001906137a4565b905080821461261f5760008660000182815481106125da576125da613484565b90600052602060002001549050808760000184815481106125fd576125fd613484565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612630576126306138bd565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105f3565b60009150506105f3565b825474010000000000000000000000000000000000000000900460ff16158061269c575081155b156126a657505050565b825460018401546fffffffffffffffffffffffffffffffff808316929116906000906126ec90700100000000000000000000000000000000900463ffffffff16426137a4565b905080156127ac578183111561272e576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546127689083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612367565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b848210156128635773ffffffffffffffffffffffffffffffffffffffff841661280b576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016108f9565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016108f9565b848310156129765760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169060009082906128a790826137a4565b6128b1878a6137a4565b6128bb91906138aa565b6128c591906138ec565b905073ffffffffffffffffffffffffffffffffffffffff861661291e576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016108f9565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016108f9565b61298085846137a4565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515611912565b6000818310612a365781611912565b5090919050565b508054612a4990613021565b6000825580601f10612a59575050565b601f0160209004906000526020600020908101906114c191905b80821115612a875760008155600101612a73565b5090565b600060208284031215612a9d57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461191257600080fd5b803567ffffffffffffffff81168114612ae557600080fd5b919050565b600060208284031215612afc57600080fd5b61191282612acd565b6000815180845260005b81811015612b2b57602081850181015186830182015201612b0f565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006119126020830184612b05565b73ffffffffffffffffffffffffffffffffffffffff811681146114c157600080fd5b8035612ae581612b7c565b600060208284031215612bbb57600080fd5b813561191281612b7c565b600060208284031215612bd857600080fd5b813567ffffffffffffffff811115612bef57600080fd5b8201610100818503121561191257600080fd5b60008083601f840112612c1457600080fd5b50813567ffffffffffffffff811115612c2c57600080fd5b6020830191508360208260051b8501011115612c4757600080fd5b9250929050565b60008060008060408587031215612c6457600080fd5b843567ffffffffffffffff80821115612c7c57600080fd5b612c8888838901612c02565b90965094506020870135915080821115612ca157600080fd5b50612cae87828801612c02565b95989497509550505050565b600080600060408486031215612ccf57600080fd5b612cd884612acd565b9250602084013567ffffffffffffffff80821115612cf557600080fd5b818601915086601f830112612d0957600080fd5b813581811115612d1857600080fd5b876020828501011115612d2a57600080fd5b6020830194508093505050509250925092565b600060208284031215612d4f57600080fd5b813567ffffffffffffffff811115612d6657600080fd5b820160a0818503121561191257600080fd5b602081526000825160406020840152612d946060840182612b05565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152612dcf8282612b05565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015612e2657835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612df4565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612e2657835167ffffffffffffffff1683529284019291840191600101612e4e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612ec757612ec7612e74565b60405290565b60405160c0810167ffffffffffffffff81118282101715612ec757612ec7612e74565b80151581146114c157600080fd5b8035612ae581612ef0565b80356fffffffffffffffffffffffffffffffff81168114612ae557600080fd5b600060608284031215612f3b57600080fd5b6040516060810181811067ffffffffffffffff82111715612f5e57612f5e612e74565b6040529050808235612f6f81612ef0565b8152612f7d60208401612f09565b6020820152612f8e60408401612f09565b60408201525092915050565b600080600060e08486031215612faf57600080fd5b612fb884612acd565b9250612fc78560208601612f29565b9150612fd68560808601612f29565b90509250925092565b60008060208385031215612ff257600080fd5b823567ffffffffffffffff81111561300957600080fd5b61301585828601612c02565b90969095509350505050565b600181811c9082168061303557607f821691505b60208210810361306e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261308557600080fd5b813567ffffffffffffffff808211156130a0576130a0612e74565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156130e6576130e6612e74565b816040528381528660208588010111156130ff57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000610100823603121561313257600080fd5b61313a612ea3565b823567ffffffffffffffff8082111561315257600080fd5b61315e36838701613074565b835261316c60208601612acd565b602084015261317d60408601612b9e565b60408401526060850135606084015261319860808601612b9e565b608084015260a08501359150808211156131b157600080fd5b6131bd36838701613074565b60a084015260c08501359150808211156131d657600080fd5b6131e236838701613074565b60c084015260e08501359150808211156131fb57600080fd5b5061320836828601613074565b60e08301525092915050565b601f821115611025576000816000526020600020601f850160051c8101602086101561323d5750805b601f850160051c820191505b8181101561325c57828155600101613249565b505050505050565b67ffffffffffffffff83111561327c5761327c612e74565b6132908361328a8354613021565b83613214565b6000601f8411600181146132e257600085156132ac5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b85565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156133315786850135825560209485019460019092019101613311565b508682101561336c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b6040815260006133916040830186612b05565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a082360312156133f457600080fd5b60405160a0810167ffffffffffffffff828210818311171561341857613418612e74565b81604052843591508082111561342d57600080fd5b5061343a36828601613074565b82525061344960208401612acd565b6020820152604083013561345c81612b7c565b604082015260608381013590820152608083013561347981612b7c565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec18336030181126134e757600080fd5b9190910192915050565b6000610140823603121561350457600080fd5b61350c612ecd565b61351583612acd565b815261352360208401612efe565b6020820152604083013567ffffffffffffffff8082111561354357600080fd5b61354f36838701613074565b6040840152606085013591508082111561356857600080fd5b5061357536828601613074565b6060830152506135883660808501612f29565b608082015261359a3660e08501612f29565b60a082015292915050565b815167ffffffffffffffff8111156135bf576135bf612e74565b6135d3816135cd8454613021565b84613214565b602080601f83116001811461362657600084156135f05750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b17855561325c565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561367357888601518255948401946001909101908401613654565b50858210156136af57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff871683528060208401526136e381840187612b05565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506137219050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152612dcf565b60006020828403121561376a57600080fd5b815161191281612ef0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f3576105f3613775565b67ffffffffffffffff8416815260e0810161380360208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612389565b606081016105f382848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561388857600080fd5b815161191281612b7c565b80820281158282048414176105f3576105f3613775565b808201808211156105f3576105f3613775565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600082613922577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", } var BurnFromMintTokenPoolABI = BurnFromMintTokenPoolMetaData.ABI @@ -2066,6 +2066,123 @@ func (_BurnFromMintTokenPool *BurnFromMintTokenPoolFilterer) ParseOwnershipTrans return event, nil } +type BurnFromMintTokenPoolRateLimitAdminSetIterator struct { + Event *BurnFromMintTokenPoolRateLimitAdminSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *BurnFromMintTokenPoolRateLimitAdminSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(BurnFromMintTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(BurnFromMintTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *BurnFromMintTokenPoolRateLimitAdminSetIterator) Error() error { + return it.fail +} + +func (it *BurnFromMintTokenPoolRateLimitAdminSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type BurnFromMintTokenPoolRateLimitAdminSet struct { + RateLimitAdmin common.Address + Raw types.Log +} + +func (_BurnFromMintTokenPool *BurnFromMintTokenPoolFilterer) FilterRateLimitAdminSet(opts *bind.FilterOpts) (*BurnFromMintTokenPoolRateLimitAdminSetIterator, error) { + + logs, sub, err := _BurnFromMintTokenPool.contract.FilterLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return &BurnFromMintTokenPoolRateLimitAdminSetIterator{contract: _BurnFromMintTokenPool.contract, event: "RateLimitAdminSet", logs: logs, sub: sub}, nil +} + +func (_BurnFromMintTokenPool *BurnFromMintTokenPoolFilterer) WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *BurnFromMintTokenPoolRateLimitAdminSet) (event.Subscription, error) { + + logs, sub, err := _BurnFromMintTokenPool.contract.WatchLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(BurnFromMintTokenPoolRateLimitAdminSet) + if err := _BurnFromMintTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_BurnFromMintTokenPool *BurnFromMintTokenPoolFilterer) ParseRateLimitAdminSet(log types.Log) (*BurnFromMintTokenPoolRateLimitAdminSet, error) { + event := new(BurnFromMintTokenPoolRateLimitAdminSet) + if err := _BurnFromMintTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type BurnFromMintTokenPoolReleasedIterator struct { Event *BurnFromMintTokenPoolReleased @@ -2591,6 +2708,8 @@ func (_BurnFromMintTokenPool *BurnFromMintTokenPool) ParseLog(log types.Log) (ge return _BurnFromMintTokenPool.ParseOwnershipTransferRequested(log) case _BurnFromMintTokenPool.abi.Events["OwnershipTransferred"].ID: return _BurnFromMintTokenPool.ParseOwnershipTransferred(log) + case _BurnFromMintTokenPool.abi.Events["RateLimitAdminSet"].ID: + return _BurnFromMintTokenPool.ParseRateLimitAdminSet(log) case _BurnFromMintTokenPool.abi.Events["Released"].ID: return _BurnFromMintTokenPool.ParseReleased(log) case _BurnFromMintTokenPool.abi.Events["RemotePoolSet"].ID: @@ -2649,6 +2768,10 @@ func (BurnFromMintTokenPoolOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (BurnFromMintTokenPoolRateLimitAdminSet) Topic() common.Hash { + return common.HexToHash("0x44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d09174") +} + func (BurnFromMintTokenPoolReleased) Topic() common.Hash { return common.HexToHash("0x2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52") } @@ -2788,6 +2911,12 @@ type BurnFromMintTokenPoolInterface interface { ParseOwnershipTransferred(log types.Log) (*BurnFromMintTokenPoolOwnershipTransferred, error) + FilterRateLimitAdminSet(opts *bind.FilterOpts) (*BurnFromMintTokenPoolRateLimitAdminSetIterator, error) + + WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *BurnFromMintTokenPoolRateLimitAdminSet) (event.Subscription, error) + + ParseRateLimitAdminSet(log types.Log) (*BurnFromMintTokenPoolRateLimitAdminSet, error) + FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*BurnFromMintTokenPoolReleasedIterator, error) WatchReleased(opts *bind.WatchOpts, sink chan<- *BurnFromMintTokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, error) diff --git a/core/gethwrappers/ccip/generated/burn_mint_token_pool/burn_mint_token_pool.go b/core/gethwrappers/ccip/generated/burn_mint_token_pool/burn_mint_token_pool.go index 777f3737a56..c43083c2585 100644 --- a/core/gethwrappers/ccip/generated/burn_mint_token_pool/burn_mint_token_pool.go +++ b/core/gethwrappers/ccip/generated/burn_mint_token_pool/burn_mint_token_pool.go @@ -82,8 +82,8 @@ type TokenPoolChainUpdate struct { } var BurnMintTokenPoolMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractIBurnMintERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60e06040523480156200001157600080fd5b5060405162003fbc38038062003fbc8339810160408190526200003491620004e5565b83838383336000816200005a57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008d576200008d8162000140565b50506001600160a01b0384161580620000ad57506001600160a01b038116155b80620000c057506001600160a01b038216155b15620000df576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013257604080516000815260208101909152620001329084620001ba565b505050505050505062000643565b336001600160a01b038216036200016a57604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001db576040516335f4a7b360e01b815260040160405180910390fd5b60005b825181101562000266576000838281518110620001ff57620001ff620005f5565b602090810291909101015190506200021960028262000317565b156200025c576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001de565b5060005b8151811015620003125760008282815181106200028b576200028b620005f5565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002b7575062000309565b620002c460028262000337565b1562000307576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b6001016200026a565b505050565b60006200032e836001600160a01b0384166200034e565b90505b92915050565b60006200032e836001600160a01b03841662000452565b6000818152600183016020526040812054801562000447576000620003756001836200060b565b85549091506000906200038b906001906200060b565b9050808214620003f7576000866000018281548110620003af57620003af620005f5565b9060005260206000200154905080876000018481548110620003d557620003d5620005f5565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806200040b576200040b6200062d565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000331565b600091505062000331565b60008181526001830160205260408120546200049b5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000331565b50600062000331565b6001600160a01b0381168114620004ba57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b8051620004e081620004a4565b919050565b60008060008060808587031215620004fc57600080fd5b84516200050981620004a4565b602086810151919550906001600160401b03808211156200052957600080fd5b818801915088601f8301126200053e57600080fd5b815181811115620005535762000553620004bd565b8060051b604051601f19603f830116810181811085821117156200057b576200057b620004bd565b60405291825284820192508381018501918b8311156200059a57600080fd5b938501935b82851015620005c357620005b385620004d3565b845293850193928501926200059f565b809850505050505050620005da60408601620004d3565b9150620005ea60608601620004d3565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b818103818111156200033157634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60805160a05160c0516138fc620006c0600039600081816104dd0152818161171801526120c50152600081816104b70152818161157901526119ce0152600081816102390152818161028e015281816106e001528181611499015281816118ee01528181611ae00152818161205b01526122b001526138fc6000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80639a4575b9116100ee578063c4bffe2b11610097578063db6327dc11610071578063db6327dc146104a2578063dc0bd971146104b5578063e0351e13146104db578063f2fde38b1461050157600080fd5b8063c4bffe2b14610467578063c75eea9c1461047c578063cf7401f31461048f57600080fd5b8063b0f479a1116100c8578063b0f479a114610423578063b794658014610441578063c0d786551461045457600080fd5b80639a4575b91461037f578063a7cd63b71461039f578063af58d59f146103b457600080fd5b806354c8a4f31161015b57806379ba50971161013557806379ba5097146103335780637d54534e1461033b5780638926f54f1461034e5780638da5cb5b1461036157600080fd5b806354c8a4f3146102ed5780636d3d1a581461030257806378a010b21461032057600080fd5b806321df0da71161018c57806321df0da714610237578063240028e81461027e57806339077537146102cb57600080fd5b806301ffc9a7146101b35780630a2fd493146101db578063181f5a77146101fb575b600080fd5b6101c66101c1366004612a53565b610514565b60405190151581526020015b60405180910390f35b6101ee6101e9366004612ab2565b6105f9565b6040516101d29190612b31565b6101ee6040518060400160405280601781526020017f4275726e4d696e74546f6b656e506f6f6c20312e352e3000000000000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b6101c661028c366004612b71565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b6102de6102d9366004612b8e565b6106a9565b604051905181526020016101d2565b6103006102fb366004612c16565b61082f565b005b60085473ffffffffffffffffffffffffffffffffffffffff16610259565b61030061032e366004612c82565b6108aa565b610300610a1e565b610300610349366004612b71565b610aec565b6101c661035c366004612ab2565b610b3b565b60015473ffffffffffffffffffffffffffffffffffffffff16610259565b61039261038d366004612d05565b610b52565b6040516101d29190612d40565b6103a7610bf9565b6040516101d29190612da0565b6103c76103c2366004612ab2565b610c0a565b6040516101d2919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff16610259565b6101ee61044f366004612ab2565b610cdf565b610300610462366004612b71565b610d0a565b61046f610de5565b6040516101d29190612dfa565b6103c761048a366004612ab2565b610e9d565b61030061049d366004612f62565b610f6f565b6103006104b0366004612fa7565b610ff8565b7f0000000000000000000000000000000000000000000000000000000000000000610259565b7f00000000000000000000000000000000000000000000000000000000000000006101c6565b61030061050f366004612b71565b61147e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806105a757507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806105f357507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061062490612fe9565b80601f016020809104026020016040519081016040528092919081815260200182805461065090612fe9565b801561069d5780601f106106725761010080835404028352916020019161069d565b820191906000526020600020905b81548152906001019060200180831161068057829003601f168201915b50505050509050919050565b6040805160208101909152600081526106c96106c4836130e7565b611492565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166340c10f196107156060850160408601612b71565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260608501356024820152604401600060405180830381600087803b15801561078557600080fd5b505af1158015610799573d6000803e3d6000fd5b506107ae925050506060830160408401612b71565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0846060013560405161081091815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6108376116c3565b6108a48484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061171692505050565b50505050565b6108b26116c3565b6108bb83610b3b565b610902576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b67ffffffffffffffff83166000908152600760205260408120600401805461092990612fe9565b80601f016020809104026020016040519081016040528092919081815260200182805461095590612fe9565b80156109a25780601f10610977576101008083540402835291602001916109a2565b820191906000526020600020905b81548152906001019060200180831161098557829003601f168201915b5050505067ffffffffffffffff86166000908152600760205260409020919250506004016109d183858361322c565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610a1093929190613346565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a6f576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610af46116c3565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60006105f3600567ffffffffffffffff84166118cc565b6040805180820190915260608082526020820152610b77610b72836133aa565b6118e7565b610b848260600135611ab1565b6040516060830135815233907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a26040518060400160405280610bde84602001602081019061044f9190612ab2565b81526040805160208181019092526000815291015292915050565b6060610c056002611b54565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526105f390611b61565b67ffffffffffffffff8116600090815260076020526040902060050180546060919061062490612fe9565b610d126116c3565b73ffffffffffffffffffffffffffffffffffffffff8116610d5f576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b60606000610df36005611b54565b90506000815167ffffffffffffffff811115610e1157610e11612e3c565b604051908082528060200260200182016040528015610e3a578160200160208202803683370190505b50905060005b8251811015610e9657828181518110610e5b57610e5b61344c565b6020026020010151828281518110610e7557610e7561344c565b67ffffffffffffffff90921660209283029190910190910152600101610e40565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526105f390611b61565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590610faf575060015473ffffffffffffffffffffffffffffffffffffffff163314155b15610fe8576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b610ff3838383611c13565b505050565b6110006116c3565b60005b81811015610ff357600083838381811061101f5761101f61344c565b9050602002810190611031919061347b565b61103a906134b9565b905061104f8160800151826020015115611cfd565b6110628160a00151826020015115611cfd565b80602001511561135e5780516110849060059067ffffffffffffffff16611e36565b6110c95780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b60408101515115806110de5750606081015151155b15611115576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906112f6908261356d565b506060820151600582019061130b908261356d565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c295506113519493929190613687565b60405180910390a1611475565b80516113769060059067ffffffffffffffff16611e42565b6113bb5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906114246004830182612a05565b611432600583016000612a05565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611003565b6114866116c3565b61148f81611e4e565b50565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146115275760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa1580156115d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f99190613720565b15611630576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d8160200151611f12565b600061164c82602001516105f9565b9050805160001480611670575080805190602001208260a001518051906020012014155b156116ad578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016108f99190612b31565b6116bf82602001518360600151612038565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611714576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f000000000000000000000000000000000000000000000000000000000000000061176d576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b825181101561180357600083828151811061178d5761178d61344c565b602002602001015190506117ab81600261207f90919063ffffffff16565b156117fa5760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611770565b5060005b8151811015610ff35760008282815181106118245761182461344c565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361186857506118c4565b6118736002826120a1565b156118c25760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611807565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161461197c5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4e9190613720565b15611a85576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a9281604001516120c3565b611a9f8160200151612142565b61148f81602001518260600151612290565b6040517f42966c68000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906342966c6890602401600060405180830381600087803b158015611b3957600080fd5b505af1158015611b4d573d6000803e3d6000fd5b5050505050565b606060006118e0836122d4565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611bef82606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611bd3919061376c565b85608001516fffffffffffffffffffffffffffffffff1661232f565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b611c1c83610b3b565b611c5e576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108f9565b611c69826000611cfd565b67ffffffffffffffff83166000908152600760205260409020611c8c9083612359565b611c97816000611cfd565b67ffffffffffffffff83166000908152600760205260409020611cbd9060020182612359565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b838383604051611cf09392919061377f565b60405180910390a1505050565b815115611dc45781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580611d53575060408201516fffffffffffffffffffffffffffffffff16155b15611d8c57816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016108f99190613802565b80156116bf576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff16151580611dfd575060208201516fffffffffffffffffffffffffffffffff1615155b156116bf57816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016108f99190613802565b60006118e083836124fb565b60006118e0838361254a565b3373ffffffffffffffffffffffffffffffffffffffff821603611e9d576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611f1b81610b3b565b611f5d576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015611fdc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120009190613720565b61148f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116bf90600201827f000000000000000000000000000000000000000000000000000000000000000061263d565b60006118e08373ffffffffffffffffffffffffffffffffffffffff841661254a565b60006118e08373ffffffffffffffffffffffffffffffffffffffff84166124fb565b7f00000000000000000000000000000000000000000000000000000000000000001561148f576120f46002826129c0565b61148f576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108f9565b61214b81610b3b565b61218d576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612206573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222a919061383e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461148f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116bf90827f000000000000000000000000000000000000000000000000000000000000000061263d565b60608160000180548060200260200160405190810160405280929190818152602001828054801561069d57602002820191906000526020600020905b8154815260200190600101908083116123105750505050509050919050565b600061234e8561233f848661385b565b6123499087613872565b6129ef565b90505b949350505050565b815460009061238290700100000000000000000000000000000000900463ffffffff164261376c565b9050801561242457600183015483546123ca916fffffffffffffffffffffffffffffffff8082169281169185917001000000000000000000000000000000009091041661232f565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b6020820151835461244a916fffffffffffffffffffffffffffffffff90811691166129ef565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990611cf0908490613802565b6000818152600183016020526040812054612542575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105f3565b5060006105f3565b6000818152600183016020526040812054801561263357600061256e60018361376c565b85549091506000906125829060019061376c565b90508082146125e75760008660000182815481106125a2576125a261344c565b90600052602060002001549050808760000184815481106125c5576125c561344c565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806125f8576125f8613885565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105f3565b60009150506105f3565b825474010000000000000000000000000000000000000000900460ff161580612664575081155b1561266e57505050565b825460018401546fffffffffffffffffffffffffffffffff808316929116906000906126b490700100000000000000000000000000000000900463ffffffff164261376c565b9050801561277457818311156126f6576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546127309083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661232f565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b8482101561282b5773ffffffffffffffffffffffffffffffffffffffff84166127d3576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016108f9565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016108f9565b8483101561293e5760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690600090829061286f908261376c565b612879878a61376c565b6128839190613872565b61288d91906138b4565b905073ffffffffffffffffffffffffffffffffffffffff86166128e6576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016108f9565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016108f9565b612948858461376c565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415156118e0565b60008183106129fe57816118e0565b5090919050565b508054612a1190612fe9565b6000825580601f10612a21575050565b601f01602090049060005260206000209081019061148f91905b80821115612a4f5760008155600101612a3b565b5090565b600060208284031215612a6557600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146118e057600080fd5b803567ffffffffffffffff81168114612aad57600080fd5b919050565b600060208284031215612ac457600080fd5b6118e082612a95565b6000815180845260005b81811015612af357602081850181015186830182015201612ad7565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006118e06020830184612acd565b73ffffffffffffffffffffffffffffffffffffffff8116811461148f57600080fd5b8035612aad81612b44565b600060208284031215612b8357600080fd5b81356118e081612b44565b600060208284031215612ba057600080fd5b813567ffffffffffffffff811115612bb757600080fd5b820161010081850312156118e057600080fd5b60008083601f840112612bdc57600080fd5b50813567ffffffffffffffff811115612bf457600080fd5b6020830191508360208260051b8501011115612c0f57600080fd5b9250929050565b60008060008060408587031215612c2c57600080fd5b843567ffffffffffffffff80821115612c4457600080fd5b612c5088838901612bca565b90965094506020870135915080821115612c6957600080fd5b50612c7687828801612bca565b95989497509550505050565b600080600060408486031215612c9757600080fd5b612ca084612a95565b9250602084013567ffffffffffffffff80821115612cbd57600080fd5b818601915086601f830112612cd157600080fd5b813581811115612ce057600080fd5b876020828501011115612cf257600080fd5b6020830194508093505050509250925092565b600060208284031215612d1757600080fd5b813567ffffffffffffffff811115612d2e57600080fd5b820160a081850312156118e057600080fd5b602081526000825160406020840152612d5c6060840182612acd565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152612d978282612acd565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015612dee57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612dbc565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612dee57835167ffffffffffffffff1683529284019291840191600101612e16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612e8f57612e8f612e3c565b60405290565b60405160c0810167ffffffffffffffff81118282101715612e8f57612e8f612e3c565b801515811461148f57600080fd5b8035612aad81612eb8565b80356fffffffffffffffffffffffffffffffff81168114612aad57600080fd5b600060608284031215612f0357600080fd5b6040516060810181811067ffffffffffffffff82111715612f2657612f26612e3c565b6040529050808235612f3781612eb8565b8152612f4560208401612ed1565b6020820152612f5660408401612ed1565b60408201525092915050565b600080600060e08486031215612f7757600080fd5b612f8084612a95565b9250612f8f8560208601612ef1565b9150612f9e8560808601612ef1565b90509250925092565b60008060208385031215612fba57600080fd5b823567ffffffffffffffff811115612fd157600080fd5b612fdd85828601612bca565b90969095509350505050565b600181811c90821680612ffd57607f821691505b602082108103613036577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261304d57600080fd5b813567ffffffffffffffff8082111561306857613068612e3c565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156130ae576130ae612e3c565b816040528381528660208588010111156130c757600080fd5b836020870160208301376000602085830101528094505050505092915050565b600061010082360312156130fa57600080fd5b613102612e6b565b823567ffffffffffffffff8082111561311a57600080fd5b6131263683870161303c565b835261313460208601612a95565b602084015261314560408601612b66565b60408401526060850135606084015261316060808601612b66565b608084015260a085013591508082111561317957600080fd5b6131853683870161303c565b60a084015260c085013591508082111561319e57600080fd5b6131aa3683870161303c565b60c084015260e08501359150808211156131c357600080fd5b506131d03682860161303c565b60e08301525092915050565b601f821115610ff3576000816000526020600020601f850160051c810160208610156132055750805b601f850160051c820191505b8181101561322457828155600101613211565b505050505050565b67ffffffffffffffff83111561324457613244612e3c565b613258836132528354612fe9565b836131dc565b6000601f8411600181146132aa57600085156132745750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b4d565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156132f957868501358255602094850194600190920191016132d9565b5086821015613334577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b6040815260006133596040830186612acd565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a082360312156133bc57600080fd5b60405160a0810167ffffffffffffffff82821081831117156133e0576133e0612e3c565b8160405284359150808211156133f557600080fd5b506134023682860161303c565b82525061341160208401612a95565b6020820152604083013561342481612b44565b604082015260608381013590820152608083013561344181612b44565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec18336030181126134af57600080fd5b9190910192915050565b600061014082360312156134cc57600080fd5b6134d4612e95565b6134dd83612a95565b81526134eb60208401612ec6565b6020820152604083013567ffffffffffffffff8082111561350b57600080fd5b6135173683870161303c565b6040840152606085013591508082111561353057600080fd5b5061353d3682860161303c565b6060830152506135503660808501612ef1565b60808201526135623660e08501612ef1565b60a082015292915050565b815167ffffffffffffffff81111561358757613587612e3c565b61359b816135958454612fe9565b846131dc565b602080601f8311600181146135ee57600084156135b85750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613224565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561363b5788860151825594840194600190910190840161361c565b508582101561367757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff871683528060208401526136ab81840187612acd565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506136e99050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152612d97565b60006020828403121561373257600080fd5b81516118e081612eb8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f3576105f361373d565b67ffffffffffffffff8416815260e081016137cb60208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612351565b606081016105f382848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561385057600080fd5b81516118e081612b44565b80820281158282048414176105f3576105f361373d565b808201808211156105f3576105f361373d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000826138ea577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"contractIBurnMintERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"RateLimitAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60e06040523480156200001157600080fd5b5060405162003fee38038062003fee8339810160408190526200003491620004e5565b83838383336000816200005a57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008d576200008d8162000140565b50506001600160a01b0384161580620000ad57506001600160a01b038116155b80620000c057506001600160a01b038216155b15620000df576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013257604080516000815260208101909152620001329084620001ba565b505050505050505062000643565b336001600160a01b038216036200016a57604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001db576040516335f4a7b360e01b815260040160405180910390fd5b60005b825181101562000266576000838281518110620001ff57620001ff620005f5565b602090810291909101015190506200021960028262000317565b156200025c576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001de565b5060005b8151811015620003125760008282815181106200028b576200028b620005f5565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002b7575062000309565b620002c460028262000337565b1562000307576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b6001016200026a565b505050565b60006200032e836001600160a01b0384166200034e565b90505b92915050565b60006200032e836001600160a01b03841662000452565b6000818152600183016020526040812054801562000447576000620003756001836200060b565b85549091506000906200038b906001906200060b565b9050808214620003f7576000866000018281548110620003af57620003af620005f5565b9060005260206000200154905080876000018481548110620003d557620003d5620005f5565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806200040b576200040b6200062d565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000331565b600091505062000331565b60008181526001830160205260408120546200049b5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000331565b50600062000331565b6001600160a01b0381168114620004ba57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b8051620004e081620004a4565b919050565b60008060008060808587031215620004fc57600080fd5b84516200050981620004a4565b602086810151919550906001600160401b03808211156200052957600080fd5b818801915088601f8301126200053e57600080fd5b815181811115620005535762000553620004bd565b8060051b604051601f19603f830116810181811085821117156200057b576200057b620004bd565b60405291825284820192508381018501918b8311156200059a57600080fd5b938501935b82851015620005c357620005b385620004d3565b845293850193928501926200059f565b809850505050505050620005da60408601620004d3565b9150620005ea60608601620004d3565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b818103818111156200033157634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60805160a05160c05161392e620006c0600039600081816104dd0152818161174a01526120f70152600081816104b7015281816115ab0152611a000152600081816102390152818161028e015281816106e0015281816114cb0152818161192001528181611b120152818161208d01526122e2015261392e6000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80639a4575b9116100ee578063c4bffe2b11610097578063db6327dc11610071578063db6327dc146104a2578063dc0bd971146104b5578063e0351e13146104db578063f2fde38b1461050157600080fd5b8063c4bffe2b14610467578063c75eea9c1461047c578063cf7401f31461048f57600080fd5b8063b0f479a1116100c8578063b0f479a114610423578063b794658014610441578063c0d786551461045457600080fd5b80639a4575b91461037f578063a7cd63b71461039f578063af58d59f146103b457600080fd5b806354c8a4f31161015b57806379ba50971161013557806379ba5097146103335780637d54534e1461033b5780638926f54f1461034e5780638da5cb5b1461036157600080fd5b806354c8a4f3146102ed5780636d3d1a581461030257806378a010b21461032057600080fd5b806321df0da71161018c57806321df0da714610237578063240028e81461027e57806339077537146102cb57600080fd5b806301ffc9a7146101b35780630a2fd493146101db578063181f5a77146101fb575b600080fd5b6101c66101c1366004612a85565b610514565b60405190151581526020015b60405180910390f35b6101ee6101e9366004612ae4565b6105f9565b6040516101d29190612b63565b6101ee6040518060400160405280601781526020017f4275726e4d696e74546f6b656e506f6f6c20312e352e3000000000000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b6101c661028c366004612ba3565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b6102de6102d9366004612bc0565b6106a9565b604051905181526020016101d2565b6103006102fb366004612c48565b61082f565b005b60085473ffffffffffffffffffffffffffffffffffffffff16610259565b61030061032e366004612cb4565b6108aa565b610300610a1e565b610300610349366004612ba3565b610aec565b6101c661035c366004612ae4565b610b6d565b60015473ffffffffffffffffffffffffffffffffffffffff16610259565b61039261038d366004612d37565b610b84565b6040516101d29190612d72565b6103a7610c2b565b6040516101d29190612dd2565b6103c76103c2366004612ae4565b610c3c565b6040516101d2919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff16610259565b6101ee61044f366004612ae4565b610d11565b610300610462366004612ba3565b610d3c565b61046f610e17565b6040516101d29190612e2c565b6103c761048a366004612ae4565b610ecf565b61030061049d366004612f94565b610fa1565b6103006104b0366004612fd9565b61102a565b7f0000000000000000000000000000000000000000000000000000000000000000610259565b7f00000000000000000000000000000000000000000000000000000000000000006101c6565b61030061050f366004612ba3565b6114b0565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806105a757507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806105f357507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff811660009081526007602052604090206004018054606091906106249061301b565b80601f01602080910402602001604051908101604052809291908181526020018280546106509061301b565b801561069d5780601f106106725761010080835404028352916020019161069d565b820191906000526020600020905b81548152906001019060200180831161068057829003601f168201915b50505050509050919050565b6040805160208101909152600081526106c96106c483613119565b6114c4565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166340c10f196107156060850160408601612ba3565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260608501356024820152604401600060405180830381600087803b15801561078557600080fd5b505af1158015610799573d6000803e3d6000fd5b506107ae925050506060830160408401612ba3565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0846060013560405161081091815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6108376116f5565b6108a48484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061174892505050565b50505050565b6108b26116f5565b6108bb83610b6d565b610902576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b67ffffffffffffffff8316600090815260076020526040812060040180546109299061301b565b80601f01602080910402602001604051908101604052809291908181526020018280546109559061301b565b80156109a25780601f10610977576101008083540402835291602001916109a2565b820191906000526020600020905b81548152906001019060200180831161098557829003601f168201915b5050505067ffffffffffffffff86166000908152600760205260409020919250506004016109d183858361325e565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610a1093929190613378565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a6f576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610af46116f5565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d091749060200160405180910390a150565b60006105f3600567ffffffffffffffff84166118fe565b6040805180820190915260608082526020820152610ba9610ba4836133dc565b611919565b610bb68260600135611ae3565b6040516060830135815233907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a26040518060400160405280610c1084602001602081019061044f9190612ae4565b81526040805160208181019092526000815291015292915050565b6060610c376002611b86565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526105f390611b93565b67ffffffffffffffff811660009081526007602052604090206005018054606091906106249061301b565b610d446116f5565b73ffffffffffffffffffffffffffffffffffffffff8116610d91576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b60606000610e256005611b86565b90506000815167ffffffffffffffff811115610e4357610e43612e6e565b604051908082528060200260200182016040528015610e6c578160200160208202803683370190505b50905060005b8251811015610ec857828181518110610e8d57610e8d61347e565b6020026020010151828281518110610ea757610ea761347e565b67ffffffffffffffff90921660209283029190910190910152600101610e72565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526105f390611b93565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590610fe1575060015473ffffffffffffffffffffffffffffffffffffffff163314155b1561101a576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b611025838383611c45565b505050565b6110326116f5565b60005b818110156110255760008383838181106110515761105161347e565b905060200281019061106391906134ad565b61106c906134eb565b90506110818160800151826020015115611d2f565b6110948160a00151826020015115611d2f565b8060200151156113905780516110b69060059067ffffffffffffffff16611e68565b6110fb5780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b60408101515115806111105750606081015151155b15611147576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c17909116961515029590951790985590810151940151938116931690910291909117600382015591519091906004820190611328908261359f565b506060820151600582019061133d908261359f565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c2955061138394939291906136b9565b60405180910390a16114a7565b80516113a89060059067ffffffffffffffff16611e74565b6113ed5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f9565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906114566004830182612a37565b611464600583016000612a37565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611035565b6114b86116f5565b6114c181611e80565b50565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146115595760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611607573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162b9190613752565b15611662576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61166f8160200151611f44565b600061167e82602001516105f9565b90508051600014806116a2575080805190602001208260a001518051906020012014155b156116df578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016108f99190612b63565b6116f18260200151836060015161206a565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611746576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f000000000000000000000000000000000000000000000000000000000000000061179f576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82518110156118355760008382815181106117bf576117bf61347e565b602002602001015190506117dd8160026120b190919063ffffffff16565b1561182c5760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b506001016117a2565b5060005b81518110156110255760008282815181106118565761185661347e565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361189a57506118f6565b6118a56002826120d3565b156118f45760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611839565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146119ae5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f9565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611a5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a809190613752565b15611ab7576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ac481604001516120f5565b611ad18160200151612174565b6114c1816020015182606001516122c2565b6040517f42966c68000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906342966c6890602401600060405180830381600087803b158015611b6b57600080fd5b505af1158015611b7f573d6000803e3d6000fd5b5050505050565b6060600061191283612306565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611c2182606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611c05919061379e565b85608001516fffffffffffffffffffffffffffffffff16612361565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b611c4e83610b6d565b611c90576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108f9565b611c9b826000611d2f565b67ffffffffffffffff83166000908152600760205260409020611cbe908361238b565b611cc9816000611d2f565b67ffffffffffffffff83166000908152600760205260409020611cef906002018261238b565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b838383604051611d22939291906137b1565b60405180910390a1505050565b815115611df65781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580611d85575060408201516fffffffffffffffffffffffffffffffff16155b15611dbe57816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016108f99190613834565b80156116f1576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff16151580611e2f575060208201516fffffffffffffffffffffffffffffffff1615155b156116f157816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016108f99190613834565b6000611912838361252d565b6000611912838361257c565b3373ffffffffffffffffffffffffffffffffffffffff821603611ecf576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611f4d81610b6d565b611f8f576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa15801561200e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120329190613752565b6114c1576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116f190600201827f000000000000000000000000000000000000000000000000000000000000000061266f565b60006119128373ffffffffffffffffffffffffffffffffffffffff841661257c565b60006119128373ffffffffffffffffffffffffffffffffffffffff841661252d565b7f0000000000000000000000000000000000000000000000000000000000000000156114c1576121266002826129f2565b6114c1576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108f9565b61217d81610b6d565b6121bf576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f9565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612238573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225c9190613870565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146114c1576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f9565b67ffffffffffffffff821660009081526007602052604090206116f190827f000000000000000000000000000000000000000000000000000000000000000061266f565b60608160000180548060200260200160405190810160405280929190818152602001828054801561069d57602002820191906000526020600020905b8154815260200190600101908083116123425750505050509050919050565b600061238085612371848661388d565b61237b90876138a4565b612a21565b90505b949350505050565b81546000906123b490700100000000000000000000000000000000900463ffffffff164261379e565b9050801561245657600183015483546123fc916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612361565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b6020820151835461247c916fffffffffffffffffffffffffffffffff9081169116612a21565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990611d22908490613834565b6000818152600183016020526040812054612574575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105f3565b5060006105f3565b600081815260018301602052604081205480156126655760006125a060018361379e565b85549091506000906125b49060019061379e565b90508082146126195760008660000182815481106125d4576125d461347e565b90600052602060002001549050808760000184815481106125f7576125f761347e565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061262a5761262a6138b7565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105f3565b60009150506105f3565b825474010000000000000000000000000000000000000000900460ff161580612696575081155b156126a057505050565b825460018401546fffffffffffffffffffffffffffffffff808316929116906000906126e690700100000000000000000000000000000000900463ffffffff164261379e565b905080156127a65781831115612728576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546127629083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612361565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b8482101561285d5773ffffffffffffffffffffffffffffffffffffffff8416612805576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016108f9565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016108f9565b848310156129705760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169060009082906128a1908261379e565b6128ab878a61379e565b6128b591906138a4565b6128bf91906138e6565b905073ffffffffffffffffffffffffffffffffffffffff8616612918576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016108f9565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016108f9565b61297a858461379e565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515611912565b6000818310612a305781611912565b5090919050565b508054612a439061301b565b6000825580601f10612a53575050565b601f0160209004906000526020600020908101906114c191905b80821115612a815760008155600101612a6d565b5090565b600060208284031215612a9757600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461191257600080fd5b803567ffffffffffffffff81168114612adf57600080fd5b919050565b600060208284031215612af657600080fd5b61191282612ac7565b6000815180845260005b81811015612b2557602081850181015186830182015201612b09565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006119126020830184612aff565b73ffffffffffffffffffffffffffffffffffffffff811681146114c157600080fd5b8035612adf81612b76565b600060208284031215612bb557600080fd5b813561191281612b76565b600060208284031215612bd257600080fd5b813567ffffffffffffffff811115612be957600080fd5b8201610100818503121561191257600080fd5b60008083601f840112612c0e57600080fd5b50813567ffffffffffffffff811115612c2657600080fd5b6020830191508360208260051b8501011115612c4157600080fd5b9250929050565b60008060008060408587031215612c5e57600080fd5b843567ffffffffffffffff80821115612c7657600080fd5b612c8288838901612bfc565b90965094506020870135915080821115612c9b57600080fd5b50612ca887828801612bfc565b95989497509550505050565b600080600060408486031215612cc957600080fd5b612cd284612ac7565b9250602084013567ffffffffffffffff80821115612cef57600080fd5b818601915086601f830112612d0357600080fd5b813581811115612d1257600080fd5b876020828501011115612d2457600080fd5b6020830194508093505050509250925092565b600060208284031215612d4957600080fd5b813567ffffffffffffffff811115612d6057600080fd5b820160a0818503121561191257600080fd5b602081526000825160406020840152612d8e6060840182612aff565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152612dc98282612aff565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015612e2057835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612dee565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612e2057835167ffffffffffffffff1683529284019291840191600101612e48565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612ec157612ec1612e6e565b60405290565b60405160c0810167ffffffffffffffff81118282101715612ec157612ec1612e6e565b80151581146114c157600080fd5b8035612adf81612eea565b80356fffffffffffffffffffffffffffffffff81168114612adf57600080fd5b600060608284031215612f3557600080fd5b6040516060810181811067ffffffffffffffff82111715612f5857612f58612e6e565b6040529050808235612f6981612eea565b8152612f7760208401612f03565b6020820152612f8860408401612f03565b60408201525092915050565b600080600060e08486031215612fa957600080fd5b612fb284612ac7565b9250612fc18560208601612f23565b9150612fd08560808601612f23565b90509250925092565b60008060208385031215612fec57600080fd5b823567ffffffffffffffff81111561300357600080fd5b61300f85828601612bfc565b90969095509350505050565b600181811c9082168061302f57607f821691505b602082108103613068577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261307f57600080fd5b813567ffffffffffffffff8082111561309a5761309a612e6e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156130e0576130e0612e6e565b816040528381528660208588010111156130f957600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000610100823603121561312c57600080fd5b613134612e9d565b823567ffffffffffffffff8082111561314c57600080fd5b6131583683870161306e565b835261316660208601612ac7565b602084015261317760408601612b98565b60408401526060850135606084015261319260808601612b98565b608084015260a08501359150808211156131ab57600080fd5b6131b73683870161306e565b60a084015260c08501359150808211156131d057600080fd5b6131dc3683870161306e565b60c084015260e08501359150808211156131f557600080fd5b506132023682860161306e565b60e08301525092915050565b601f821115611025576000816000526020600020601f850160051c810160208610156132375750805b601f850160051c820191505b8181101561325657828155600101613243565b505050505050565b67ffffffffffffffff83111561327657613276612e6e565b61328a83613284835461301b565b8361320e565b6000601f8411600181146132dc57600085156132a65750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b7f565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561332b578685013582556020948501946001909201910161330b565b5086821015613366577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b60408152600061338b6040830186612aff565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a082360312156133ee57600080fd5b60405160a0810167ffffffffffffffff828210818311171561341257613412612e6e565b81604052843591508082111561342757600080fd5b506134343682860161306e565b82525061344360208401612ac7565b6020820152604083013561345681612b76565b604082015260608381013590820152608083013561347381612b76565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec18336030181126134e157600080fd5b9190910192915050565b600061014082360312156134fe57600080fd5b613506612ec7565b61350f83612ac7565b815261351d60208401612ef8565b6020820152604083013567ffffffffffffffff8082111561353d57600080fd5b6135493683870161306e565b6040840152606085013591508082111561356257600080fd5b5061356f3682860161306e565b6060830152506135823660808501612f23565b60808201526135943660e08501612f23565b60a082015292915050565b815167ffffffffffffffff8111156135b9576135b9612e6e565b6135cd816135c7845461301b565b8461320e565b602080601f83116001811461362057600084156135ea5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613256565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561366d5788860151825594840194600190910190840161364e565b50858210156136a957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff871683528060208401526136dd81840187612aff565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff908116606087015290870151166080850152915061371b9050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152612dc9565b60006020828403121561376457600080fd5b815161191281612eea565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f3576105f361376f565b67ffffffffffffffff8416815260e081016137fd60208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612383565b606081016105f382848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561388257600080fd5b815161191281612b76565b80820281158282048414176105f3576105f361376f565b808201808211156105f3576105f361376f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008261391c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", } var BurnMintTokenPoolABI = BurnMintTokenPoolMetaData.ABI @@ -2066,6 +2066,123 @@ func (_BurnMintTokenPool *BurnMintTokenPoolFilterer) ParseOwnershipTransferred(l return event, nil } +type BurnMintTokenPoolRateLimitAdminSetIterator struct { + Event *BurnMintTokenPoolRateLimitAdminSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *BurnMintTokenPoolRateLimitAdminSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(BurnMintTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(BurnMintTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *BurnMintTokenPoolRateLimitAdminSetIterator) Error() error { + return it.fail +} + +func (it *BurnMintTokenPoolRateLimitAdminSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type BurnMintTokenPoolRateLimitAdminSet struct { + RateLimitAdmin common.Address + Raw types.Log +} + +func (_BurnMintTokenPool *BurnMintTokenPoolFilterer) FilterRateLimitAdminSet(opts *bind.FilterOpts) (*BurnMintTokenPoolRateLimitAdminSetIterator, error) { + + logs, sub, err := _BurnMintTokenPool.contract.FilterLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return &BurnMintTokenPoolRateLimitAdminSetIterator{contract: _BurnMintTokenPool.contract, event: "RateLimitAdminSet", logs: logs, sub: sub}, nil +} + +func (_BurnMintTokenPool *BurnMintTokenPoolFilterer) WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *BurnMintTokenPoolRateLimitAdminSet) (event.Subscription, error) { + + logs, sub, err := _BurnMintTokenPool.contract.WatchLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(BurnMintTokenPoolRateLimitAdminSet) + if err := _BurnMintTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_BurnMintTokenPool *BurnMintTokenPoolFilterer) ParseRateLimitAdminSet(log types.Log) (*BurnMintTokenPoolRateLimitAdminSet, error) { + event := new(BurnMintTokenPoolRateLimitAdminSet) + if err := _BurnMintTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type BurnMintTokenPoolReleasedIterator struct { Event *BurnMintTokenPoolReleased @@ -2591,6 +2708,8 @@ func (_BurnMintTokenPool *BurnMintTokenPool) ParseLog(log types.Log) (generated. return _BurnMintTokenPool.ParseOwnershipTransferRequested(log) case _BurnMintTokenPool.abi.Events["OwnershipTransferred"].ID: return _BurnMintTokenPool.ParseOwnershipTransferred(log) + case _BurnMintTokenPool.abi.Events["RateLimitAdminSet"].ID: + return _BurnMintTokenPool.ParseRateLimitAdminSet(log) case _BurnMintTokenPool.abi.Events["Released"].ID: return _BurnMintTokenPool.ParseReleased(log) case _BurnMintTokenPool.abi.Events["RemotePoolSet"].ID: @@ -2649,6 +2768,10 @@ func (BurnMintTokenPoolOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (BurnMintTokenPoolRateLimitAdminSet) Topic() common.Hash { + return common.HexToHash("0x44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d09174") +} + func (BurnMintTokenPoolReleased) Topic() common.Hash { return common.HexToHash("0x2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52") } @@ -2788,6 +2911,12 @@ type BurnMintTokenPoolInterface interface { ParseOwnershipTransferred(log types.Log) (*BurnMintTokenPoolOwnershipTransferred, error) + FilterRateLimitAdminSet(opts *bind.FilterOpts) (*BurnMintTokenPoolRateLimitAdminSetIterator, error) + + WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *BurnMintTokenPoolRateLimitAdminSet) (event.Subscription, error) + + ParseRateLimitAdminSet(log types.Log) (*BurnMintTokenPoolRateLimitAdminSet, error) + FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*BurnMintTokenPoolReleasedIterator, error) WatchReleased(opts *bind.WatchOpts, sink chan<- *BurnMintTokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, error) diff --git a/core/gethwrappers/ccip/generated/burn_with_from_mint_token_pool/burn_with_from_mint_token_pool.go b/core/gethwrappers/ccip/generated/burn_with_from_mint_token_pool/burn_with_from_mint_token_pool.go index a7ceb0e68a1..22409787fd8 100644 --- a/core/gethwrappers/ccip/generated/burn_with_from_mint_token_pool/burn_with_from_mint_token_pool.go +++ b/core/gethwrappers/ccip/generated/burn_with_from_mint_token_pool/burn_with_from_mint_token_pool.go @@ -82,8 +82,8 @@ type TokenPoolChainUpdate struct { } var BurnWithFromMintTokenPoolMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractIBurnMintERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x60e06040523480156200001157600080fd5b50604051620044153803806200441583398101604081905262000034916200085d565b83838383336000816200005a57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008d576200008d8162000159565b50506001600160a01b0384161580620000ad57506001600160a01b038116155b80620000c057506001600160a01b038216155b15620000df576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013257604080516000815260208101909152620001329084620001d3565b506200014f925050506001600160a01b0385163060001962000330565b5050505062000a99565b336001600160a01b038216036200018357604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001f4576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200027f5760008382815181106200021857620002186200096d565b602090810291909101015190506200023260028262000416565b1562000275576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001f7565b5060005b81518110156200032b576000828281518110620002a457620002a46200096d565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002d0575062000322565b620002dd60028262000436565b1562000320576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000283565b505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801562000382573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003a8919062000983565b620003b49190620009b3565b604080516001600160a01b038616602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915291925062000410918691906200044d16565b50505050565b60006200042d836001600160a01b03841662000522565b90505b92915050565b60006200042d836001600160a01b03841662000626565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200049c906001600160a01b03851690849062000678565b8051909150156200032b5780806020019051810190620004bd9190620009c9565b6200032b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b600081815260018301602052604081205480156200061b57600062000549600183620009f4565b85549091506000906200055f90600190620009f4565b9050808214620005cb5760008660000182815481106200058357620005836200096d565b9060005260206000200154905080876000018481548110620005a957620005a96200096d565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080620005df57620005df62000a0a565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000430565b600091505062000430565b60008181526001830160205260408120546200066f5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000430565b50600062000430565b606062000689848460008562000691565b949350505050565b606082471015620006f45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000519565b600080866001600160a01b0316858760405162000712919062000a46565b60006040518083038185875af1925050503d806000811462000751576040519150601f19603f3d011682016040523d82523d6000602084013e62000756565b606091505b5090925090506200076a8783838762000775565b979650505050505050565b60608315620007e9578251600003620007e1576001600160a01b0385163b620007e15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000519565b508162000689565b620006898383815115620008005781518083602001fd5b8060405162461bcd60e51b815260040162000519919062000a64565b6001600160a01b03811681146200083257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b805162000858816200081c565b919050565b600080600080608085870312156200087457600080fd5b845162000881816200081c565b602086810151919550906001600160401b0380821115620008a157600080fd5b818801915088601f830112620008b657600080fd5b815181811115620008cb57620008cb62000835565b8060051b604051601f19603f83011681018181108582111715620008f357620008f362000835565b60405291825284820192508381018501918b8311156200091257600080fd5b938501935b828510156200093b576200092b856200084b565b8452938501939285019262000917565b80985050505050505062000952604086016200084b565b915062000962606086016200084b565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156200099657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808201808211156200043057620004306200099d565b600060208284031215620009dc57600080fd5b81518015158114620009ed57600080fd5b9392505050565b818103818111156200043057620004306200099d565b634e487b7160e01b600052603160045260246000fd5b60005b8381101562000a3d57818101518382015260200162000a23565b50506000910152565b6000825162000a5a81846020870162000a20565b9190910192915050565b602081526000825180602084015262000a8581604085016020870162000a20565b601f01601f19169190910160400192915050565b60805160a05160c0516138ff62000b16600039600081816104da0152818161171501526120c80152600081816104b40152818161157601526119cb0152600081816102360152818161028b015281816106dd01528181611496015281816118eb01528181611ae30152818161205e01526122b301526138ff6000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80639a4575b9116100ee578063c4bffe2b11610097578063db6327dc11610071578063db6327dc1461049f578063dc0bd971146104b2578063e0351e13146104d8578063f2fde38b146104fe57600080fd5b8063c4bffe2b14610464578063c75eea9c14610479578063cf7401f31461048c57600080fd5b8063b0f479a1116100c8578063b0f479a114610420578063b79465801461043e578063c0d786551461045157600080fd5b80639a4575b91461037c578063a7cd63b71461039c578063af58d59f146103b157600080fd5b806354c8a4f31161015b57806379ba50971161013557806379ba5097146103305780637d54534e146103385780638926f54f1461034b5780638da5cb5b1461035e57600080fd5b806354c8a4f3146102ea5780636d3d1a58146102ff57806378a010b21461031d57600080fd5b806321df0da71161018c57806321df0da714610234578063240028e81461027b57806339077537146102c857600080fd5b806301ffc9a7146101b35780630a2fd493146101db578063181f5a77146101fb575b600080fd5b6101c66101c1366004612a56565b610511565b60405190151581526020015b60405180910390f35b6101ee6101e9366004612ab5565b6105f6565b6040516101d29190612b34565b60408051808201909152601f81527f4275726e5769746846726f6d4d696e74546f6b656e506f6f6c20312e352e300060208201526101ee565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b6101c6610289366004612b74565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b6102db6102d6366004612b91565b6106a6565b604051905181526020016101d2565b6102fd6102f8366004612c19565b61082c565b005b60085473ffffffffffffffffffffffffffffffffffffffff16610256565b6102fd61032b366004612c85565b6108a7565b6102fd610a1b565b6102fd610346366004612b74565b610ae9565b6101c6610359366004612ab5565b610b38565b60015473ffffffffffffffffffffffffffffffffffffffff16610256565b61038f61038a366004612d08565b610b4f565b6040516101d29190612d43565b6103a4610bf6565b6040516101d29190612da3565b6103c46103bf366004612ab5565b610c07565b6040516101d2919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff16610256565b6101ee61044c366004612ab5565b610cdc565b6102fd61045f366004612b74565b610d07565b61046c610de2565b6040516101d29190612dfd565b6103c4610487366004612ab5565b610e9a565b6102fd61049a366004612f65565b610f6c565b6102fd6104ad366004612faa565b610ff5565b7f0000000000000000000000000000000000000000000000000000000000000000610256565b7f00000000000000000000000000000000000000000000000000000000000000006101c6565b6102fd61050c366004612b74565b61147b565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806105a457507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806105f057507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061062190612fec565b80601f016020809104026020016040519081016040528092919081815260200182805461064d90612fec565b801561069a5780601f1061066f5761010080835404028352916020019161069a565b820191906000526020600020905b81548152906001019060200180831161067d57829003601f168201915b50505050509050919050565b6040805160208101909152600081526106c66106c1836130ea565b61148f565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166340c10f196107126060850160408601612b74565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260608501356024820152604401600060405180830381600087803b15801561078257600080fd5b505af1158015610796573d6000803e3d6000fd5b506107ab925050506060830160408401612b74565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0846060013560405161080d91815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6108346116c0565b6108a18484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061171392505050565b50505050565b6108af6116c0565b6108b883610b38565b6108ff576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b67ffffffffffffffff83166000908152600760205260408120600401805461092690612fec565b80601f016020809104026020016040519081016040528092919081815260200182805461095290612fec565b801561099f5780601f106109745761010080835404028352916020019161099f565b820191906000526020600020905b81548152906001019060200180831161098257829003601f168201915b5050505067ffffffffffffffff86166000908152600760205260409020919250506004016109ce83858361322f565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610a0d93929190613349565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a6c576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610af16116c0565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60006105f0600567ffffffffffffffff84166118c9565b6040805180820190915260608082526020820152610b74610b6f836133ad565b6118e4565b610b818260600135611aae565b6040516060830135815233907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a26040518060400160405280610bdb84602001602081019061044c9190612ab5565b81526040805160208181019092526000815291015292915050565b6060610c026002611b57565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526105f090611b64565b67ffffffffffffffff8116600090815260076020526040902060050180546060919061062190612fec565b610d0f6116c0565b73ffffffffffffffffffffffffffffffffffffffff8116610d5c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b60606000610df06005611b57565b90506000815167ffffffffffffffff811115610e0e57610e0e612e3f565b604051908082528060200260200182016040528015610e37578160200160208202803683370190505b50905060005b8251811015610e9357828181518110610e5857610e5861344f565b6020026020010151828281518110610e7257610e7261344f565b67ffffffffffffffff90921660209283029190910190910152600101610e3d565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526105f090611b64565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590610fac575060015473ffffffffffffffffffffffffffffffffffffffff163314155b15610fe5576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016108f6565b610ff0838383611c16565b505050565b610ffd6116c0565b60005b81811015610ff057600083838381811061101c5761101c61344f565b905060200281019061102e919061347e565b611037906134bc565b905061104c8160800151826020015115611d00565b61105f8160a00151826020015115611d00565b80602001511561135b5780516110819060059067ffffffffffffffff16611e39565b6110c65780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f6565b60408101515115806110db5750606081015151155b15611112576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906112f39082613570565b50606082015160058201906113089082613570565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c2955061134e949392919061368a565b60405180910390a1611472565b80516113739060059067ffffffffffffffff16611e45565b6113b85780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f6565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906114216004830182612a08565b61142f600583016000612a08565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611000565b6114836116c0565b61148c81611e51565b50565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146115245760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f6565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa1580156115d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f69190613723565b1561162d576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163a8160200151611f15565b600061164982602001516105f6565b905080516000148061166d575080805190602001208260a001518051906020012014155b156116aa578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016108f69190612b34565b6116bc8260200151836060015161203b565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611711576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f000000000000000000000000000000000000000000000000000000000000000061176a576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b825181101561180057600083828151811061178a5761178a61344f565b602002602001015190506117a881600261208290919063ffffffff16565b156117f75760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b5060010161176d565b5060005b8151811015610ff05760008282815181106118215761182161344f565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361186557506118c1565b6118706002826120a4565b156118bf5760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611804565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146119795760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f6565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611a27573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4b9190613723565b15611a82576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a8f81604001516120c6565b611a9c8160200151612145565b61148c81602001518260600151612293565b6040517f9dc29fac000000000000000000000000000000000000000000000000000000008152306004820152602481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639dc29fac90604401600060405180830381600087803b158015611b3c57600080fd5b505af1158015611b50573d6000803e3d6000fd5b5050505050565b606060006118dd836122d7565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611bf282606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611bd6919061376f565b85608001516fffffffffffffffffffffffffffffffff16612332565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b611c1f83610b38565b611c61576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108f6565b611c6c826000611d00565b67ffffffffffffffff83166000908152600760205260409020611c8f908361235c565b611c9a816000611d00565b67ffffffffffffffff83166000908152600760205260409020611cc0906002018261235c565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b838383604051611cf393929190613782565b60405180910390a1505050565b815115611dc75781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580611d56575060408201516fffffffffffffffffffffffffffffffff16155b15611d8f57816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016108f69190613805565b80156116bc576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff16151580611e00575060208201516fffffffffffffffffffffffffffffffff1615155b156116bc57816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016108f69190613805565b60006118dd83836124fe565b60006118dd838361254d565b3373ffffffffffffffffffffffffffffffffffffffff821603611ea0576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611f1e81610b38565b611f60576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f6565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015611fdf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120039190613723565b61148c576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f6565b67ffffffffffffffff821660009081526007602052604090206116bc90600201827f0000000000000000000000000000000000000000000000000000000000000000612640565b60006118dd8373ffffffffffffffffffffffffffffffffffffffff841661254d565b60006118dd8373ffffffffffffffffffffffffffffffffffffffff84166124fe565b7f00000000000000000000000000000000000000000000000000000000000000001561148c576120f76002826129c3565b61148c576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108f6565b61214e81610b38565b612190576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f6565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612209573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061222d9190613841565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461148c576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f6565b67ffffffffffffffff821660009081526007602052604090206116bc90827f0000000000000000000000000000000000000000000000000000000000000000612640565b60608160000180548060200260200160405190810160405280929190818152602001828054801561069a57602002820191906000526020600020905b8154815260200190600101908083116123135750505050509050919050565b600061235185612342848661385e565b61234c9087613875565b6129f2565b90505b949350505050565b815460009061238590700100000000000000000000000000000000900463ffffffff164261376f565b9050801561242757600183015483546123cd916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612332565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b6020820151835461244d916fffffffffffffffffffffffffffffffff90811691166129f2565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990611cf3908490613805565b6000818152600183016020526040812054612545575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105f0565b5060006105f0565b6000818152600183016020526040812054801561263657600061257160018361376f565b85549091506000906125859060019061376f565b90508082146125ea5760008660000182815481106125a5576125a561344f565b90600052602060002001549050808760000184815481106125c8576125c861344f565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806125fb576125fb613888565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105f0565b60009150506105f0565b825474010000000000000000000000000000000000000000900460ff161580612667575081155b1561267157505050565b825460018401546fffffffffffffffffffffffffffffffff808316929116906000906126b790700100000000000000000000000000000000900463ffffffff164261376f565b9050801561277757818311156126f9576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546127339083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612332565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b8482101561282e5773ffffffffffffffffffffffffffffffffffffffff84166127d6576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016108f6565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016108f6565b848310156129415760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16906000908290612872908261376f565b61287c878a61376f565b6128869190613875565b61289091906138b7565b905073ffffffffffffffffffffffffffffffffffffffff86166128e9576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016108f6565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016108f6565b61294b858461376f565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415156118dd565b6000818310612a0157816118dd565b5090919050565b508054612a1490612fec565b6000825580601f10612a24575050565b601f01602090049060005260206000209081019061148c91905b80821115612a525760008155600101612a3e565b5090565b600060208284031215612a6857600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146118dd57600080fd5b803567ffffffffffffffff81168114612ab057600080fd5b919050565b600060208284031215612ac757600080fd5b6118dd82612a98565b6000815180845260005b81811015612af657602081850181015186830182015201612ada565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006118dd6020830184612ad0565b73ffffffffffffffffffffffffffffffffffffffff8116811461148c57600080fd5b8035612ab081612b47565b600060208284031215612b8657600080fd5b81356118dd81612b47565b600060208284031215612ba357600080fd5b813567ffffffffffffffff811115612bba57600080fd5b820161010081850312156118dd57600080fd5b60008083601f840112612bdf57600080fd5b50813567ffffffffffffffff811115612bf757600080fd5b6020830191508360208260051b8501011115612c1257600080fd5b9250929050565b60008060008060408587031215612c2f57600080fd5b843567ffffffffffffffff80821115612c4757600080fd5b612c5388838901612bcd565b90965094506020870135915080821115612c6c57600080fd5b50612c7987828801612bcd565b95989497509550505050565b600080600060408486031215612c9a57600080fd5b612ca384612a98565b9250602084013567ffffffffffffffff80821115612cc057600080fd5b818601915086601f830112612cd457600080fd5b813581811115612ce357600080fd5b876020828501011115612cf557600080fd5b6020830194508093505050509250925092565b600060208284031215612d1a57600080fd5b813567ffffffffffffffff811115612d3157600080fd5b820160a081850312156118dd57600080fd5b602081526000825160406020840152612d5f6060840182612ad0565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152612d9a8282612ad0565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015612df157835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612dbf565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612df157835167ffffffffffffffff1683529284019291840191600101612e19565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612e9257612e92612e3f565b60405290565b60405160c0810167ffffffffffffffff81118282101715612e9257612e92612e3f565b801515811461148c57600080fd5b8035612ab081612ebb565b80356fffffffffffffffffffffffffffffffff81168114612ab057600080fd5b600060608284031215612f0657600080fd5b6040516060810181811067ffffffffffffffff82111715612f2957612f29612e3f565b6040529050808235612f3a81612ebb565b8152612f4860208401612ed4565b6020820152612f5960408401612ed4565b60408201525092915050565b600080600060e08486031215612f7a57600080fd5b612f8384612a98565b9250612f928560208601612ef4565b9150612fa18560808601612ef4565b90509250925092565b60008060208385031215612fbd57600080fd5b823567ffffffffffffffff811115612fd457600080fd5b612fe085828601612bcd565b90969095509350505050565b600181811c9082168061300057607f821691505b602082108103613039577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261305057600080fd5b813567ffffffffffffffff8082111561306b5761306b612e3f565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156130b1576130b1612e3f565b816040528381528660208588010111156130ca57600080fd5b836020870160208301376000602085830101528094505050505092915050565b600061010082360312156130fd57600080fd5b613105612e6e565b823567ffffffffffffffff8082111561311d57600080fd5b6131293683870161303f565b835261313760208601612a98565b602084015261314860408601612b69565b60408401526060850135606084015261316360808601612b69565b608084015260a085013591508082111561317c57600080fd5b6131883683870161303f565b60a084015260c08501359150808211156131a157600080fd5b6131ad3683870161303f565b60c084015260e08501359150808211156131c657600080fd5b506131d33682860161303f565b60e08301525092915050565b601f821115610ff0576000816000526020600020601f850160051c810160208610156132085750805b601f850160051c820191505b8181101561322757828155600101613214565b505050505050565b67ffffffffffffffff83111561324757613247612e3f565b61325b836132558354612fec565b836131df565b6000601f8411600181146132ad57600085156132775750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b50565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156132fc57868501358255602094850194600190920191016132dc565b5086821015613337577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b60408152600061335c6040830186612ad0565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a082360312156133bf57600080fd5b60405160a0810167ffffffffffffffff82821081831117156133e3576133e3612e3f565b8160405284359150808211156133f857600080fd5b506134053682860161303f565b82525061341460208401612a98565b6020820152604083013561342781612b47565b604082015260608381013590820152608083013561344481612b47565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec18336030181126134b257600080fd5b9190910192915050565b600061014082360312156134cf57600080fd5b6134d7612e98565b6134e083612a98565b81526134ee60208401612ec9565b6020820152604083013567ffffffffffffffff8082111561350e57600080fd5b61351a3683870161303f565b6040840152606085013591508082111561353357600080fd5b506135403682860161303f565b6060830152506135533660808501612ef4565b60808201526135653660e08501612ef4565b60a082015292915050565b815167ffffffffffffffff81111561358a5761358a612e3f565b61359e816135988454612fec565b846131df565b602080601f8311600181146135f157600084156135bb5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613227565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561363e5788860151825594840194600190910190840161361f565b508582101561367a57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff871683528060208401526136ae81840187612ad0565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506136ec9050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152612d9a565b60006020828403121561373557600080fd5b81516118dd81612ebb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f0576105f0613740565b67ffffffffffffffff8416815260e081016137ce60208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612354565b606081016105f082848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561385357600080fd5b81516118dd81612b47565b80820281158282048414176105f0576105f0613740565b808201808211156105f0576105f0613740565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000826138ed577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"contractIBurnMintERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"RateLimitAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x60e06040523480156200001157600080fd5b50604051620044473803806200444783398101604081905262000034916200085d565b83838383336000816200005a57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008d576200008d8162000159565b50506001600160a01b0384161580620000ad57506001600160a01b038116155b80620000c057506001600160a01b038216155b15620000df576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013257604080516000815260208101909152620001329084620001d3565b506200014f925050506001600160a01b0385163060001962000330565b5050505062000a99565b336001600160a01b038216036200018357604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001f4576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200027f5760008382815181106200021857620002186200096d565b602090810291909101015190506200023260028262000416565b1562000275576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001f7565b5060005b81518110156200032b576000828281518110620002a457620002a46200096d565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002d0575062000322565b620002dd60028262000436565b1562000320576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000283565b505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801562000382573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003a8919062000983565b620003b49190620009b3565b604080516001600160a01b038616602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915291925062000410918691906200044d16565b50505050565b60006200042d836001600160a01b03841662000522565b90505b92915050565b60006200042d836001600160a01b03841662000626565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200049c906001600160a01b03851690849062000678565b8051909150156200032b5780806020019051810190620004bd9190620009c9565b6200032b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b600081815260018301602052604081205480156200061b57600062000549600183620009f4565b85549091506000906200055f90600190620009f4565b9050808214620005cb5760008660000182815481106200058357620005836200096d565b9060005260206000200154905080876000018481548110620005a957620005a96200096d565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080620005df57620005df62000a0a565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000430565b600091505062000430565b60008181526001830160205260408120546200066f5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000430565b50600062000430565b606062000689848460008562000691565b949350505050565b606082471015620006f45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000519565b600080866001600160a01b0316858760405162000712919062000a46565b60006040518083038185875af1925050503d806000811462000751576040519150601f19603f3d011682016040523d82523d6000602084013e62000756565b606091505b5090925090506200076a8783838762000775565b979650505050505050565b60608315620007e9578251600003620007e1576001600160a01b0385163b620007e15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000519565b508162000689565b620006898383815115620008005781518083602001fd5b8060405162461bcd60e51b815260040162000519919062000a64565b6001600160a01b03811681146200083257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b805162000858816200081c565b919050565b600080600080608085870312156200087457600080fd5b845162000881816200081c565b602086810151919550906001600160401b0380821115620008a157600080fd5b818801915088601f830112620008b657600080fd5b815181811115620008cb57620008cb62000835565b8060051b604051601f19603f83011681018181108582111715620008f357620008f362000835565b60405291825284820192508381018501918b8311156200091257600080fd5b938501935b828510156200093b576200092b856200084b565b8452938501939285019262000917565b80985050505050505062000952604086016200084b565b915062000962606086016200084b565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156200099657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808201808211156200043057620004306200099d565b600060208284031215620009dc57600080fd5b81518015158114620009ed57600080fd5b9392505050565b818103818111156200043057620004306200099d565b634e487b7160e01b600052603160045260246000fd5b60005b8381101562000a3d57818101518382015260200162000a23565b50506000910152565b6000825162000a5a81846020870162000a20565b9190910192915050565b602081526000825180602084015262000a8581604085016020870162000a20565b601f01601f19169190910160400192915050565b60805160a05160c05161393162000b16600039600081816104da0152818161174701526120fa0152600081816104b4015281816115a801526119fd0152600081816102360152818161028b015281816106dd015281816114c80152818161191d01528181611b150152818161209001526122e501526139316000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80639a4575b9116100ee578063c4bffe2b11610097578063db6327dc11610071578063db6327dc1461049f578063dc0bd971146104b2578063e0351e13146104d8578063f2fde38b146104fe57600080fd5b8063c4bffe2b14610464578063c75eea9c14610479578063cf7401f31461048c57600080fd5b8063b0f479a1116100c8578063b0f479a114610420578063b79465801461043e578063c0d786551461045157600080fd5b80639a4575b91461037c578063a7cd63b71461039c578063af58d59f146103b157600080fd5b806354c8a4f31161015b57806379ba50971161013557806379ba5097146103305780637d54534e146103385780638926f54f1461034b5780638da5cb5b1461035e57600080fd5b806354c8a4f3146102ea5780636d3d1a58146102ff57806378a010b21461031d57600080fd5b806321df0da71161018c57806321df0da714610234578063240028e81461027b57806339077537146102c857600080fd5b806301ffc9a7146101b35780630a2fd493146101db578063181f5a77146101fb575b600080fd5b6101c66101c1366004612a88565b610511565b60405190151581526020015b60405180910390f35b6101ee6101e9366004612ae7565b6105f6565b6040516101d29190612b66565b60408051808201909152601f81527f4275726e5769746846726f6d4d696e74546f6b656e506f6f6c20312e352e300060208201526101ee565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b6101c6610289366004612ba6565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b6102db6102d6366004612bc3565b6106a6565b604051905181526020016101d2565b6102fd6102f8366004612c4b565b61082c565b005b60085473ffffffffffffffffffffffffffffffffffffffff16610256565b6102fd61032b366004612cb7565b6108a7565b6102fd610a1b565b6102fd610346366004612ba6565b610ae9565b6101c6610359366004612ae7565b610b6a565b60015473ffffffffffffffffffffffffffffffffffffffff16610256565b61038f61038a366004612d3a565b610b81565b6040516101d29190612d75565b6103a4610c28565b6040516101d29190612dd5565b6103c46103bf366004612ae7565b610c39565b6040516101d2919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff16610256565b6101ee61044c366004612ae7565b610d0e565b6102fd61045f366004612ba6565b610d39565b61046c610e14565b6040516101d29190612e2f565b6103c4610487366004612ae7565b610ecc565b6102fd61049a366004612f97565b610f9e565b6102fd6104ad366004612fdc565b611027565b7f0000000000000000000000000000000000000000000000000000000000000000610256565b7f00000000000000000000000000000000000000000000000000000000000000006101c6565b6102fd61050c366004612ba6565b6114ad565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806105a457507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806105f057507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff811660009081526007602052604090206004018054606091906106219061301e565b80601f016020809104026020016040519081016040528092919081815260200182805461064d9061301e565b801561069a5780601f1061066f5761010080835404028352916020019161069a565b820191906000526020600020905b81548152906001019060200180831161067d57829003601f168201915b50505050509050919050565b6040805160208101909152600081526106c66106c18361311c565b6114c1565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166340c10f196107126060850160408601612ba6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff909116600482015260608501356024820152604401600060405180830381600087803b15801561078257600080fd5b505af1158015610796573d6000803e3d6000fd5b506107ab925050506060830160408401612ba6565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f0846060013560405161080d91815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6108346116f2565b6108a18484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505060408051602080880282810182019093528782529093508792508691829185019084908082843760009201919091525061174592505050565b50505050565b6108af6116f2565b6108b883610b6a565b6108ff576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b67ffffffffffffffff8316600090815260076020526040812060040180546109269061301e565b80601f01602080910402602001604051908101604052809291908181526020018280546109529061301e565b801561099f5780601f106109745761010080835404028352916020019161099f565b820191906000526020600020905b81548152906001019060200180831161098257829003601f168201915b5050505067ffffffffffffffff86166000908152600760205260409020919250506004016109ce838583613261565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610a0d9392919061337b565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a6c576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610af16116f2565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d091749060200160405180910390a150565b60006105f0600567ffffffffffffffff84166118fb565b6040805180820190915260608082526020820152610ba6610ba1836133df565b611916565b610bb38260600135611ae0565b6040516060830135815233907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a26040518060400160405280610c0d84602001602081019061044c9190612ae7565b81526040805160208181019092526000815291015292915050565b6060610c346002611b89565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526105f090611b96565b67ffffffffffffffff811660009081526007602052604090206005018054606091906106219061301e565b610d416116f2565b73ffffffffffffffffffffffffffffffffffffffff8116610d8e576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b60606000610e226005611b89565b90506000815167ffffffffffffffff811115610e4057610e40612e71565b604051908082528060200260200182016040528015610e69578160200160208202803683370190505b50905060005b8251811015610ec557828181518110610e8a57610e8a613481565b6020026020010151828281518110610ea457610ea4613481565b67ffffffffffffffff90921660209283029190910190910152600101610e6f565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526105f090611b96565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590610fde575060015473ffffffffffffffffffffffffffffffffffffffff163314155b15611017576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016108f6565b611022838383611c48565b505050565b61102f6116f2565b60005b8181101561102257600083838381811061104e5761104e613481565b905060200281019061106091906134b0565b611069906134ee565b905061107e8160800151826020015115611d32565b6110918160a00151826020015115611d32565b80602001511561138d5780516110b39060059067ffffffffffffffff16611e6b565b6110f85780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f6565b604081015151158061110d5750606081015151155b15611144576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c1790911696151502959095179098559081015194015193811693169091029190911760038201559151909190600482019061132590826135a2565b506060820151600582019061133a90826135a2565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c2955061138094939291906136bc565b60405180910390a16114a4565b80516113a59060059067ffffffffffffffff16611e77565b6113ea5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108f6565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906114536004830182612a3a565b611461600583016000612a3a565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611032565b6114b56116f2565b6114be81611e83565b50565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146115565760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f6565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611604573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116289190613755565b1561165f576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61166c8160200151611f47565b600061167b82602001516105f6565b905080516000148061169f575080805190602001208260a001518051906020012014155b156116dc578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016108f69190612b66565b6116ee8260200151836060015161206d565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611743576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f000000000000000000000000000000000000000000000000000000000000000061179c576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82518110156118325760008382815181106117bc576117bc613481565b602002602001015190506117da8160026120b490919063ffffffff16565b156118295760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b5060010161179f565b5060005b815181101561102257600082828151811061185357611853613481565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361189757506118f3565b6118a26002826120d6565b156118f15760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611836565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146119ab5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108f6565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611a59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7d9190613755565b15611ab4576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ac181604001516120f8565b611ace8160200151612177565b6114be816020015182606001516122c5565b6040517f9dc29fac000000000000000000000000000000000000000000000000000000008152306004820152602481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639dc29fac90604401600060405180830381600087803b158015611b6e57600080fd5b505af1158015611b82573d6000803e3d6000fd5b5050505050565b6060600061190f83612309565b6040805160a081018252600080825260208201819052918101829052606081018290526080810191909152611c2482606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642611c0891906137a1565b85608001516fffffffffffffffffffffffffffffffff16612364565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b611c5183610b6a565b611c93576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016108f6565b611c9e826000611d32565b67ffffffffffffffff83166000908152600760205260409020611cc1908361238e565b611ccc816000611d32565b67ffffffffffffffff83166000908152600760205260409020611cf2906002018261238e565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b838383604051611d25939291906137b4565b60405180910390a1505050565b815115611df95781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580611d88575060408201516fffffffffffffffffffffffffffffffff16155b15611dc157816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016108f69190613837565b80156116ee576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff16151580611e32575060208201516fffffffffffffffffffffffffffffffff1615155b156116ee57816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016108f69190613837565b600061190f8383612530565b600061190f838361257f565b3373ffffffffffffffffffffffffffffffffffffffff821603611ed2576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611f5081610b6a565b611f92576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f6565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015612011573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120359190613755565b6114be576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f6565b67ffffffffffffffff821660009081526007602052604090206116ee90600201827f0000000000000000000000000000000000000000000000000000000000000000612672565b600061190f8373ffffffffffffffffffffffffffffffffffffffff841661257f565b600061190f8373ffffffffffffffffffffffffffffffffffffffff8416612530565b7f0000000000000000000000000000000000000000000000000000000000000000156114be576121296002826129f5565b6114be576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108f6565b61218081610b6a565b6121c2576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016108f6565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa15801561223b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225f9190613873565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146114be576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016108f6565b67ffffffffffffffff821660009081526007602052604090206116ee90827f0000000000000000000000000000000000000000000000000000000000000000612672565b60608160000180548060200260200160405190810160405280929190818152602001828054801561069a57602002820191906000526020600020905b8154815260200190600101908083116123455750505050509050919050565b6000612383856123748486613890565b61237e90876138a7565b612a24565b90505b949350505050565b81546000906123b790700100000000000000000000000000000000900463ffffffff16426137a1565b9050801561245957600183015483546123ff916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612364565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b6020820151835461247f916fffffffffffffffffffffffffffffffff9081169116612a24565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c1990611d25908490613837565b6000818152600183016020526040812054612577575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105f0565b5060006105f0565b600081815260018301602052604081205480156126685760006125a36001836137a1565b85549091506000906125b7906001906137a1565b905080821461261c5760008660000182815481106125d7576125d7613481565b90600052602060002001549050808760000184815481106125fa576125fa613481565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061262d5761262d6138ba565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105f0565b60009150506105f0565b825474010000000000000000000000000000000000000000900460ff161580612699575081155b156126a357505050565b825460018401546fffffffffffffffffffffffffffffffff808316929116906000906126e990700100000000000000000000000000000000900463ffffffff16426137a1565b905080156127a9578183111561272b576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546127659083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612364565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b848210156128605773ffffffffffffffffffffffffffffffffffffffff8416612808576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016108f6565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016108f6565b848310156129735760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff169060009082906128a490826137a1565b6128ae878a6137a1565b6128b891906138a7565b6128c291906138e9565b905073ffffffffffffffffffffffffffffffffffffffff861661291b576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016108f6565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016108f6565b61297d85846137a1565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561190f565b6000818310612a33578161190f565b5090919050565b508054612a469061301e565b6000825580601f10612a56575050565b601f0160209004906000526020600020908101906114be91905b80821115612a845760008155600101612a70565b5090565b600060208284031215612a9a57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461190f57600080fd5b803567ffffffffffffffff81168114612ae257600080fd5b919050565b600060208284031215612af957600080fd5b61190f82612aca565b6000815180845260005b81811015612b2857602081850181015186830182015201612b0c565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061190f6020830184612b02565b73ffffffffffffffffffffffffffffffffffffffff811681146114be57600080fd5b8035612ae281612b79565b600060208284031215612bb857600080fd5b813561190f81612b79565b600060208284031215612bd557600080fd5b813567ffffffffffffffff811115612bec57600080fd5b8201610100818503121561190f57600080fd5b60008083601f840112612c1157600080fd5b50813567ffffffffffffffff811115612c2957600080fd5b6020830191508360208260051b8501011115612c4457600080fd5b9250929050565b60008060008060408587031215612c6157600080fd5b843567ffffffffffffffff80821115612c7957600080fd5b612c8588838901612bff565b90965094506020870135915080821115612c9e57600080fd5b50612cab87828801612bff565b95989497509550505050565b600080600060408486031215612ccc57600080fd5b612cd584612aca565b9250602084013567ffffffffffffffff80821115612cf257600080fd5b818601915086601f830112612d0657600080fd5b813581811115612d1557600080fd5b876020828501011115612d2757600080fd5b6020830194508093505050509250925092565b600060208284031215612d4c57600080fd5b813567ffffffffffffffff811115612d6357600080fd5b820160a0818503121561190f57600080fd5b602081526000825160406020840152612d916060840182612b02565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152612dcc8282612b02565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015612e2357835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101612df1565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015612e2357835167ffffffffffffffff1683529284019291840191600101612e4b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715612ec457612ec4612e71565b60405290565b60405160c0810167ffffffffffffffff81118282101715612ec457612ec4612e71565b80151581146114be57600080fd5b8035612ae281612eed565b80356fffffffffffffffffffffffffffffffff81168114612ae257600080fd5b600060608284031215612f3857600080fd5b6040516060810181811067ffffffffffffffff82111715612f5b57612f5b612e71565b6040529050808235612f6c81612eed565b8152612f7a60208401612f06565b6020820152612f8b60408401612f06565b60408201525092915050565b600080600060e08486031215612fac57600080fd5b612fb584612aca565b9250612fc48560208601612f26565b9150612fd38560808601612f26565b90509250925092565b60008060208385031215612fef57600080fd5b823567ffffffffffffffff81111561300657600080fd5b61301285828601612bff565b90969095509350505050565b600181811c9082168061303257607f821691505b60208210810361306b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261308257600080fd5b813567ffffffffffffffff8082111561309d5761309d612e71565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156130e3576130e3612e71565b816040528381528660208588010111156130fc57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000610100823603121561312f57600080fd5b613137612ea0565b823567ffffffffffffffff8082111561314f57600080fd5b61315b36838701613071565b835261316960208601612aca565b602084015261317a60408601612b9b565b60408401526060850135606084015261319560808601612b9b565b608084015260a08501359150808211156131ae57600080fd5b6131ba36838701613071565b60a084015260c08501359150808211156131d357600080fd5b6131df36838701613071565b60c084015260e08501359150808211156131f857600080fd5b5061320536828601613071565b60e08301525092915050565b601f821115611022576000816000526020600020601f850160051c8101602086101561323a5750805b601f850160051c820191505b8181101561325957828155600101613246565b505050505050565b67ffffffffffffffff83111561327957613279612e71565b61328d83613287835461301e565b83613211565b6000601f8411600181146132df57600085156132a95750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b82565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561332e578685013582556020948501946001909201910161330e565b5086821015613369577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b60408152600061338e6040830186612b02565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a082360312156133f157600080fd5b60405160a0810167ffffffffffffffff828210818311171561341557613415612e71565b81604052843591508082111561342a57600080fd5b5061343736828601613071565b82525061344660208401612aca565b6020820152604083013561345981612b79565b604082015260608381013590820152608083013561347681612b79565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec18336030181126134e457600080fd5b9190910192915050565b6000610140823603121561350157600080fd5b613509612eca565b61351283612aca565b815261352060208401612efb565b6020820152604083013567ffffffffffffffff8082111561354057600080fd5b61354c36838701613071565b6040840152606085013591508082111561356557600080fd5b5061357236828601613071565b6060830152506135853660808501612f26565b60808201526135973660e08501612f26565b60a082015292915050565b815167ffffffffffffffff8111156135bc576135bc612e71565b6135d0816135ca845461301e565b84613211565b602080601f83116001811461362357600084156135ed5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613259565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561367057888601518255948401946001909101908401613651565b50858210156136ac57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff871683528060208401526136e081840187612b02565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff908116606087015290870151166080850152915061371e9050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152612dcc565b60006020828403121561376757600080fd5b815161190f81612eed565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156105f0576105f0613772565b67ffffffffffffffff8416815260e0810161380060208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612386565b606081016105f082848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561388557600080fd5b815161190f81612b79565b80820281158282048414176105f0576105f0613772565b808201808211156105f0576105f0613772565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008261391f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", } var BurnWithFromMintTokenPoolABI = BurnWithFromMintTokenPoolMetaData.ABI @@ -2066,6 +2066,123 @@ func (_BurnWithFromMintTokenPool *BurnWithFromMintTokenPoolFilterer) ParseOwners return event, nil } +type BurnWithFromMintTokenPoolRateLimitAdminSetIterator struct { + Event *BurnWithFromMintTokenPoolRateLimitAdminSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *BurnWithFromMintTokenPoolRateLimitAdminSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(BurnWithFromMintTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(BurnWithFromMintTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *BurnWithFromMintTokenPoolRateLimitAdminSetIterator) Error() error { + return it.fail +} + +func (it *BurnWithFromMintTokenPoolRateLimitAdminSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type BurnWithFromMintTokenPoolRateLimitAdminSet struct { + RateLimitAdmin common.Address + Raw types.Log +} + +func (_BurnWithFromMintTokenPool *BurnWithFromMintTokenPoolFilterer) FilterRateLimitAdminSet(opts *bind.FilterOpts) (*BurnWithFromMintTokenPoolRateLimitAdminSetIterator, error) { + + logs, sub, err := _BurnWithFromMintTokenPool.contract.FilterLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return &BurnWithFromMintTokenPoolRateLimitAdminSetIterator{contract: _BurnWithFromMintTokenPool.contract, event: "RateLimitAdminSet", logs: logs, sub: sub}, nil +} + +func (_BurnWithFromMintTokenPool *BurnWithFromMintTokenPoolFilterer) WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *BurnWithFromMintTokenPoolRateLimitAdminSet) (event.Subscription, error) { + + logs, sub, err := _BurnWithFromMintTokenPool.contract.WatchLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(BurnWithFromMintTokenPoolRateLimitAdminSet) + if err := _BurnWithFromMintTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_BurnWithFromMintTokenPool *BurnWithFromMintTokenPoolFilterer) ParseRateLimitAdminSet(log types.Log) (*BurnWithFromMintTokenPoolRateLimitAdminSet, error) { + event := new(BurnWithFromMintTokenPoolRateLimitAdminSet) + if err := _BurnWithFromMintTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type BurnWithFromMintTokenPoolReleasedIterator struct { Event *BurnWithFromMintTokenPoolReleased @@ -2591,6 +2708,8 @@ func (_BurnWithFromMintTokenPool *BurnWithFromMintTokenPool) ParseLog(log types. return _BurnWithFromMintTokenPool.ParseOwnershipTransferRequested(log) case _BurnWithFromMintTokenPool.abi.Events["OwnershipTransferred"].ID: return _BurnWithFromMintTokenPool.ParseOwnershipTransferred(log) + case _BurnWithFromMintTokenPool.abi.Events["RateLimitAdminSet"].ID: + return _BurnWithFromMintTokenPool.ParseRateLimitAdminSet(log) case _BurnWithFromMintTokenPool.abi.Events["Released"].ID: return _BurnWithFromMintTokenPool.ParseReleased(log) case _BurnWithFromMintTokenPool.abi.Events["RemotePoolSet"].ID: @@ -2649,6 +2768,10 @@ func (BurnWithFromMintTokenPoolOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (BurnWithFromMintTokenPoolRateLimitAdminSet) Topic() common.Hash { + return common.HexToHash("0x44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d09174") +} + func (BurnWithFromMintTokenPoolReleased) Topic() common.Hash { return common.HexToHash("0x2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52") } @@ -2788,6 +2911,12 @@ type BurnWithFromMintTokenPoolInterface interface { ParseOwnershipTransferred(log types.Log) (*BurnWithFromMintTokenPoolOwnershipTransferred, error) + FilterRateLimitAdminSet(opts *bind.FilterOpts) (*BurnWithFromMintTokenPoolRateLimitAdminSetIterator, error) + + WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *BurnWithFromMintTokenPoolRateLimitAdminSet) (event.Subscription, error) + + ParseRateLimitAdminSet(log types.Log) (*BurnWithFromMintTokenPoolRateLimitAdminSet, error) + FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*BurnWithFromMintTokenPoolReleasedIterator, error) WatchReleased(opts *bind.WatchOpts, sink chan<- *BurnWithFromMintTokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, error) diff --git a/core/gethwrappers/ccip/generated/lock_release_token_pool/lock_release_token_pool.go b/core/gethwrappers/ccip/generated/lock_release_token_pool/lock_release_token_pool.go index fdcb6baa4e6..307decf3b2c 100644 --- a/core/gethwrappers/ccip/generated/lock_release_token_pool/lock_release_token_pool.go +++ b/core/gethwrappers/ccip/generated/lock_release_token_pool/lock_release_token_pool.go @@ -82,8 +82,8 @@ type TokenPoolChainUpdate struct { } var LockReleaseTokenPoolMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"acceptLiquidity\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientLiquidity\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidityNotAccepted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"provider\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LiquidityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"provider\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LiquidityRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LiquidityTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"canAcceptLiquidity\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRebalancer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"provideLiquidity\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rebalancer\",\"type\":\"address\"}],\"name\":\"setRebalancer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferLiquidity\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawLiquidity\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6101006040523480156200001257600080fd5b50604051620048b7380380620048b78339810160408190526200003591620004fe565b84848483336000816200005b57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008e576200008e8162000148565b50506001600160a01b0384161580620000ae57506001600160a01b038116155b80620000c157506001600160a01b038216155b15620000e0576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013357604080516000815260208101909152620001339084620001c2565b5050505090151560e052506200066f92505050565b336001600160a01b038216036200017257604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001e3576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200026e57600083828151811062000207576200020762000621565b60209081029190910101519050620002216002826200031f565b1562000264576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001e6565b5060005b81518110156200031a57600082828151811062000293576200029362000621565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002bf575062000311565b620002cc6002826200033f565b156200030f576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000272565b505050565b600062000336836001600160a01b03841662000356565b90505b92915050565b600062000336836001600160a01b0384166200045a565b600081815260018301602052604081205480156200044f5760006200037d60018362000637565b8554909150600090620003939060019062000637565b9050808214620003ff576000866000018281548110620003b757620003b762000621565b9060005260206000200154905080876000018481548110620003dd57620003dd62000621565b6000918252602080832090910192909255918252600188019052604090208390555b855486908062000413576200041362000659565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000339565b600091505062000339565b6000818152600183016020526040812054620004a35750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000339565b50600062000339565b6001600160a01b0381168114620004c257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b8051620004e881620004ac565b919050565b80518015158114620004e857600080fd5b600080600080600060a086880312156200051757600080fd5b85516200052481620004ac565b602087810151919650906001600160401b03808211156200054457600080fd5b818901915089601f8301126200055957600080fd5b8151818111156200056e576200056e620004c5565b8060051b604051601f19603f83011681018181108582111715620005965762000596620004c5565b60405291825284820192508381018501918c831115620005b557600080fd5b938501935b82851015620005de57620005ce85620004db565b84529385019392850192620005ba565b809950505050505050620005f560408701620004db565b92506200060560608701620004ed565b91506200061560808701620004db565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b818103818111156200033957634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60805160a05160c05160e0516141ac6200070b600039600081816104ef015261171301526000818161059c01528181611c7f01526126f301526000818161057601528181611ae00152611f35015260008181610290015281816102e50152818161077a0152818161084c015281816108ed015281816117d501528181611a0001528181611e550152818161268901526128de01526141ac6000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c80638da5cb5b1161010f578063c4bffe2b116100a2578063dc0bd97111610071578063dc0bd97114610574578063e0351e131461059a578063eb521a4c146105c0578063f2fde38b146105d357600080fd5b8063c4bffe2b14610526578063c75eea9c1461053b578063cf7401f31461054e578063db6327dc1461056157600080fd5b8063b0f479a1116100de578063b0f479a1146104bc578063b7946580146104da578063bb98546b146104ed578063c0d786551461051357600080fd5b80638da5cb5b146103fa5780639a4575b914610418578063a7cd63b714610438578063af58d59f1461044d57600080fd5b806354c8a4f31161018757806378a010b21161015657806378a010b2146103b957806379ba5097146103cc5780637d54534e146103d45780638926f54f146103e757600080fd5b806354c8a4f31461036257806366320087146103755780636cfd1553146103885780636d3d1a581461039b57600080fd5b806321df0da7116101c357806321df0da71461028e578063240028e8146102d55780633907753714610322578063432a6ba31461034457600080fd5b806301ffc9a7146101f55780630a2fd4931461021d5780630a861f2a1461023d578063181f5a7714610252575b600080fd5b610208610203366004613288565b6105e6565b60405190151581526020015b60405180910390f35b61023061022b3660046132e7565b610642565b6040516102149190613370565b61025061024b366004613383565b6106f2565b005b6102306040518060400160405280601a81526020017f4c6f636b52656c65617365546f6b656e506f6f6c20312e352e3000000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610214565b6102086102e33660046133c9565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b6103356103303660046133e6565b6108a3565b60405190518152602001610214565b60095473ffffffffffffffffffffffffffffffffffffffff166102b0565b61025061037036600461346e565b6109a9565b6102506103833660046134da565b610a24565b6102506103963660046133c9565b610b00565b60085473ffffffffffffffffffffffffffffffffffffffff166102b0565b6102506103c7366004613506565b610b4f565b610250610cbe565b6102506103e23660046133c9565b610d8c565b6102086103f53660046132e7565b610ddb565b60015473ffffffffffffffffffffffffffffffffffffffff166102b0565b61042b610426366004613589565b610df2565b60405161021491906135c4565b610440610e8c565b6040516102149190613624565b61046061045b3660046132e7565b610e9d565b604051610214919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff166102b0565b6102306104e83660046132e7565b610f72565b7f0000000000000000000000000000000000000000000000000000000000000000610208565b6102506105213660046133c9565b610f9d565b61052e611078565b604051610214919061367e565b6104606105493660046132e7565b611130565b61025061055c3660046137e6565b611202565b61025061056f36600461382b565b61128b565b7f00000000000000000000000000000000000000000000000000000000000000006102b0565b7f0000000000000000000000000000000000000000000000000000000000000000610208565b6102506105ce366004613383565b611711565b6102506105e13660046133c9565b61182d565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe1d4056600000000000000000000000000000000000000000000000000000000148061063c575061063c82611841565b92915050565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061066d9061386d565b80601f01602080910402602001604051908101604052809291908181526020018280546106999061386d565b80156106e65780601f106106bb576101008083540402835291602001916106e6565b820191906000526020600020905b8154815290600101906020018083116106c957829003601f168201915b50505050509050919050565b60095473ffffffffffffffffffffffffffffffffffffffff16331461074a576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa1580156107d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fa91906138c0565b1015610832576040517fbb55fd2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61087373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163383611925565b604051819033907fc2c3f06e49b9f15e7b4af9055e183b0d73362e033ad82a07dec9bf984017171990600090a350565b6040805160208101909152600081526108c36108be83613984565b6119f9565b6109186108d660608401604085016133c9565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906060850135611925565b61092860608301604084016133c9565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52846060013560405161098a91815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6109b1611c2a565b610a1e84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250611c7d92505050565b50505050565b610a2c611c2a565b6040517f0a861f2a0000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff831690630a861f2a90602401600060405180830381600087803b158015610a9457600080fd5b505af1158015610aa8573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff167f6fa7abcf1345d1d478e5ea0da6b5f26a90eadb0546ef15ed3833944fbfd1db6282604051610af491815260200190565b60405180910390a25050565b610b08611c2a565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b610b57611c2a565b610b6083610ddb565b610ba2576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610741565b67ffffffffffffffff831660009081526007602052604081206004018054610bc99061386d565b80601f0160208091040260200160405190810160405280929190818152602001828054610bf59061386d565b8015610c425780601f10610c1757610100808354040283529160200191610c42565b820191906000526020600020905b815481529060010190602001808311610c2557829003601f168201915b5050505067ffffffffffffffff8616600090815260076020526040902091925050600401610c71838583613ac9565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610cb093929190613be4565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610d0f576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610d94611c2a565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600061063c600567ffffffffffffffff8416611e33565b6040805180820190915260608082526020820152610e17610e1283613c48565b611e4e565b6040516060830135815233907f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd600089060200160405180910390a26040518060400160405280610e718460200160208101906104e891906132e7565b81526040805160208181019092526000815291015292915050565b6060610e986002612018565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff16151594820194909452600390910154808416606083015291909104909116608082015261063c90612025565b67ffffffffffffffff8116600090815260076020526040902060050180546060919061066d9061386d565b610fa5611c2a565b73ffffffffffffffffffffffffffffffffffffffff8116610ff2576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b606060006110866005612018565b90506000815167ffffffffffffffff8111156110a4576110a46136c0565b6040519080825280602002602001820160405280156110cd578160200160208202803683370190505b50905060005b8251811015611129578281815181106110ee576110ee613cea565b602002602001015182828151811061110857611108613cea565b67ffffffffffffffff909216602092830291909101909101526001016110d3565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff16151594820194909452600190910154808416606083015291909104909116608082015261063c90612025565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590611242575060015473ffffffffffffffffffffffffffffffffffffffff163314155b1561127b576040517f8e4a23d6000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b6112868383836120d7565b505050565b611293611c2a565b60005b818110156112865760008383838181106112b2576112b2613cea565b90506020028101906112c49190613d19565b6112cd90613d57565b90506112e281608001518260200151156121c1565b6112f58160a001518260200151156121c1565b8060200151156115f15780516113179060059067ffffffffffffffff166122fa565b61135c5780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610741565b60408101515115806113715750606081015151155b156113a8576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906115899082613e0b565b506060820151600582019061159e9082613e0b565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c295506115e49493929190613f25565b60405180910390a1611708565b80516116099060059067ffffffffffffffff16612306565b61164e5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610741565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906116b7600483018261323a565b6116c560058301600061323a565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611296565b7f0000000000000000000000000000000000000000000000000000000000000000611768576040517fe93f8fa400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095473ffffffffffffffffffffffffffffffffffffffff1633146117bb576040517f8e4a23d6000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b6117fd73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333084612312565b604051819033907fc17cea59c2955cb181b03393209566960365771dbba9dc3d510180e7cb31208890600090a350565b611835611c2a565b61183e81612370565b50565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806118d457507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b8061063c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526112869084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612434565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116911614611a8e5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610741565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611b3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b609190613fbe565b15611b97576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ba48160200151612540565b6000611bb38260200151610642565b9050805160001480611bd7575080805190602001208260a001518051906020012014155b15611c14578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016107419190613370565b611c2682602001518360600151612666565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611c7b576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f0000000000000000000000000000000000000000000000000000000000000000611cd4576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8251811015611d6a576000838281518110611cf457611cf4613cea565b60200260200101519050611d128160026126ad90919063ffffffff16565b15611d615760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611cd7565b5060005b8151811015611286576000828281518110611d8b57611d8b613cea565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611dcf5750611e2b565b611dda6002826126cf565b15611e295760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611d6e565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116911614611ee35760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610741565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611f91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb59190613fbe565b15611fec576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ff981604001516126f1565b6120068160200151612770565b61183e816020015182606001516128be565b60606000611e4783612902565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526120b382606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff1642612097919061400a565b85608001516fffffffffffffffffffffffffffffffff1661295d565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b6120e083610ddb565b612122576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610741565b61212d8260006121c1565b67ffffffffffffffff831660009081526007602052604090206121509083612987565b61215b8160006121c1565b67ffffffffffffffff831660009081526007602052604090206121819060020182612987565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b8383836040516121b49392919061401d565b60405180910390a1505050565b8151156122885781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580612217575060408201516fffffffffffffffffffffffffffffffff16155b1561225057816040517f8020d12400000000000000000000000000000000000000000000000000000000815260040161074191906140a0565b8015611c26576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff161515806122c1575060208201516fffffffffffffffffffffffffffffffff1615155b15611c2657816040517fd68af9cc00000000000000000000000000000000000000000000000000000000815260040161074191906140a0565b6000611e478383612b29565b6000611e478383612b78565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610a1e9085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611977565b3373ffffffffffffffffffffffffffffffffffffffff8216036123bf576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000612496826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612c6b9092919063ffffffff16565b80519091501561128657808060200190518101906124b49190613fbe565b611286576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610741565b61254981610ddb565b61258b576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610741565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa15801561260a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061262e9190613fbe565b61183e576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b67ffffffffffffffff82166000908152600760205260409020611c2690600201827f0000000000000000000000000000000000000000000000000000000000000000612c7a565b6000611e478373ffffffffffffffffffffffffffffffffffffffff8416612b78565b6000611e478373ffffffffffffffffffffffffffffffffffffffff8416612b29565b7f00000000000000000000000000000000000000000000000000000000000000001561183e57612722600282612ffd565b61183e576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610741565b61277981610ddb565b6127bb576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610741565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612834573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285891906140dc565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461183e576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b67ffffffffffffffff82166000908152600760205260409020611c2690827f0000000000000000000000000000000000000000000000000000000000000000612c7a565b6060816000018054806020026020016040519081016040528092919081815260200182805480156106e657602002820191906000526020600020905b81548152602001906001019080831161293e5750505050509050919050565b600061297c8561296d84866140f9565b6129779087614110565b61302c565b90505b949350505050565b81546000906129b090700100000000000000000000000000000000900463ffffffff164261400a565b90508015612a5257600183015483546129f8916fffffffffffffffffffffffffffffffff8082169281169185917001000000000000000000000000000000009091041661295d565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612a78916fffffffffffffffffffffffffffffffff908116911661302c565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c19906121b49084906140a0565b6000818152600183016020526040812054612b705750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561063c565b50600061063c565b60008181526001830160205260408120548015612c61576000612b9c60018361400a565b8554909150600090612bb09060019061400a565b9050808214612c15576000866000018281548110612bd057612bd0613cea565b9060005260206000200154905080876000018481548110612bf357612bf3613cea565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612c2657612c26614123565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061063c565b600091505061063c565b606061297f8484600085613042565b825474010000000000000000000000000000000000000000900460ff161580612ca1575081155b15612cab57505050565b825460018401546fffffffffffffffffffffffffffffffff80831692911690600090612cf190700100000000000000000000000000000000900463ffffffff164261400a565b90508015612db15781831115612d33576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001860154612d6d9083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661295d565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b84821015612e685773ffffffffffffffffffffffffffffffffffffffff8416612e10576040517ff94ebcd10000000000000000000000000000000000000000000000000000000081526004810183905260248101869052604401610741565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff85166044820152606401610741565b84831015612f7b5760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16906000908290612eac908261400a565b612eb6878a61400a565b612ec09190614110565b612eca9190614152565b905073ffffffffffffffffffffffffffffffffffffffff8616612f23576040517f15279c080000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610741565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff87166044820152606401610741565b612f85858461400a565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515611e47565b600081831061303b5781611e47565b5090919050565b6060824710156130d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610741565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516130fd919061418d565b60006040518083038185875af1925050503d806000811461313a576040519150601f19603f3d011682016040523d82523d6000602084013e61313f565b606091505b50915091506131508783838761315b565b979650505050505050565b606083156131f15782516000036131ea5773ffffffffffffffffffffffffffffffffffffffff85163b6131ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610741565b508161297f565b61297f83838151156132065781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107419190613370565b5080546132469061386d565b6000825580601f10613256575050565b601f01602090049060005260206000209081019061183e91905b808211156132845760008155600101613270565b5090565b60006020828403121561329a57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611e4757600080fd5b803567ffffffffffffffff811681146132e257600080fd5b919050565b6000602082840312156132f957600080fd5b611e47826132ca565b60005b8381101561331d578181015183820152602001613305565b50506000910152565b6000815180845261333e816020860160208601613302565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611e476020830184613326565b60006020828403121561339557600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461183e57600080fd5b80356132e28161339c565b6000602082840312156133db57600080fd5b8135611e478161339c565b6000602082840312156133f857600080fd5b813567ffffffffffffffff81111561340f57600080fd5b82016101008185031215611e4757600080fd5b60008083601f84011261343457600080fd5b50813567ffffffffffffffff81111561344c57600080fd5b6020830191508360208260051b850101111561346757600080fd5b9250929050565b6000806000806040858703121561348457600080fd5b843567ffffffffffffffff8082111561349c57600080fd5b6134a888838901613422565b909650945060208701359150808211156134c157600080fd5b506134ce87828801613422565b95989497509550505050565b600080604083850312156134ed57600080fd5b82356134f88161339c565b946020939093013593505050565b60008060006040848603121561351b57600080fd5b613524846132ca565b9250602084013567ffffffffffffffff8082111561354157600080fd5b818601915086601f83011261355557600080fd5b81358181111561356457600080fd5b87602082850101111561357657600080fd5b6020830194508093505050509250925092565b60006020828403121561359b57600080fd5b813567ffffffffffffffff8111156135b257600080fd5b820160a08185031215611e4757600080fd5b6020815260008251604060208401526135e06060840182613326565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084830301604085015261361b8282613326565b95945050505050565b6020808252825182820181905260009190848201906040850190845b8181101561367257835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613640565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561367257835167ffffffffffffffff168352928401929184019160010161369a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715613713576137136136c0565b60405290565b60405160c0810167ffffffffffffffff81118282101715613713576137136136c0565b801515811461183e57600080fd5b80356132e28161373c565b80356fffffffffffffffffffffffffffffffff811681146132e257600080fd5b60006060828403121561378757600080fd5b6040516060810181811067ffffffffffffffff821117156137aa576137aa6136c0565b60405290508082356137bb8161373c565b81526137c960208401613755565b60208201526137da60408401613755565b60408201525092915050565b600080600060e084860312156137fb57600080fd5b613804846132ca565b92506138138560208601613775565b91506138228560808601613775565b90509250925092565b6000806020838503121561383e57600080fd5b823567ffffffffffffffff81111561385557600080fd5b61386185828601613422565b90969095509350505050565b600181811c9082168061388157607f821691505b6020821081036138ba577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156138d257600080fd5b5051919050565b600082601f8301126138ea57600080fd5b813567ffffffffffffffff80821115613905576139056136c0565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561394b5761394b6136c0565b8160405283815286602085880101111561396457600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000610100823603121561399757600080fd5b61399f6136ef565b823567ffffffffffffffff808211156139b757600080fd5b6139c3368387016138d9565b83526139d1602086016132ca565b60208401526139e2604086016133be565b6040840152606085013560608401526139fd608086016133be565b608084015260a0850135915080821115613a1657600080fd5b613a22368387016138d9565b60a084015260c0850135915080821115613a3b57600080fd5b613a47368387016138d9565b60c084015260e0850135915080821115613a6057600080fd5b50613a6d368286016138d9565b60e08301525092915050565b601f821115611286576000816000526020600020601f850160051c81016020861015613aa25750805b601f850160051c820191505b81811015613ac157828155600101613aae565b505050505050565b67ffffffffffffffff831115613ae157613ae16136c0565b613af583613aef835461386d565b83613a79565b6000601f841160018114613b475760008515613b115750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613bdd565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613b965786850135825560209485019460019092019101613b76565b5086821015613bd1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b604081526000613bf76040830186613326565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a08236031215613c5a57600080fd5b60405160a0810167ffffffffffffffff8282108183111715613c7e57613c7e6136c0565b816040528435915080821115613c9357600080fd5b50613ca0368286016138d9565b825250613caf602084016132ca565b60208201526040830135613cc28161339c565b6040820152606083810135908201526080830135613cdf8161339c565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec1833603018112613d4d57600080fd5b9190910192915050565b60006101408236031215613d6a57600080fd5b613d72613719565b613d7b836132ca565b8152613d896020840161374a565b6020820152604083013567ffffffffffffffff80821115613da957600080fd5b613db5368387016138d9565b60408401526060850135915080821115613dce57600080fd5b50613ddb368286016138d9565b606083015250613dee3660808501613775565b6080820152613e003660e08501613775565b60a082015292915050565b815167ffffffffffffffff811115613e2557613e256136c0565b613e3981613e33845461386d565b84613a79565b602080601f831160018114613e8c5760008415613e565750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613ac1565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613ed957888601518255948401946001909101908401613eba565b5085821015613f1557878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff87168352806020840152613f4981840187613326565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff9081166060870152908701511660808501529150613f879050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e083015261361b565b600060208284031215613fd057600080fd5b8151611e478161373c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561063c5761063c613fdb565b67ffffffffffffffff8416815260e0810161406960208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c083015261297f565b6060810161063c82848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b6000602082840312156140ee57600080fd5b8151611e478161339c565b808202811582820484141761063c5761063c613fdb565b8082018082111561063c5761063c613fdb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600082614188577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60008251613d4d81846020870161330256fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"acceptLiquidity\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientLiquidity\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidityNotAccepted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"provider\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LiquidityAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"provider\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LiquidityRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"LiquidityTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"RateLimitAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"canAcceptLiquidity\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRebalancer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"provideLiquidity\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rebalancer\",\"type\":\"address\"}],\"name\":\"setRebalancer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferLiquidity\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawLiquidity\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6101006040523480156200001257600080fd5b50604051620048e9380380620048e98339810160408190526200003591620004fe565b84848483336000816200005b57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008e576200008e8162000148565b50506001600160a01b0384161580620000ae57506001600160a01b038116155b80620000c157506001600160a01b038216155b15620000e0576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200013357604080516000815260208101909152620001339084620001c2565b5050505090151560e052506200066f92505050565b336001600160a01b038216036200017257604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c051620001e3576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200026e57600083828151811062000207576200020762000621565b60209081029190910101519050620002216002826200031f565b1562000264576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101620001e6565b5060005b81518110156200031a57600082828151811062000293576200029362000621565b6020026020010151905060006001600160a01b0316816001600160a01b031603620002bf575062000311565b620002cc6002826200033f565b156200030f576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000272565b505050565b600062000336836001600160a01b03841662000356565b90505b92915050565b600062000336836001600160a01b0384166200045a565b600081815260018301602052604081205480156200044f5760006200037d60018362000637565b8554909150600090620003939060019062000637565b9050808214620003ff576000866000018281548110620003b757620003b762000621565b9060005260206000200154905080876000018481548110620003dd57620003dd62000621565b6000918252602080832090910192909255918252600188019052604090208390555b855486908062000413576200041362000659565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000339565b600091505062000339565b6000818152600183016020526040812054620004a35750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000339565b50600062000339565b6001600160a01b0381168114620004c257600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b8051620004e881620004ac565b919050565b80518015158114620004e857600080fd5b600080600080600060a086880312156200051757600080fd5b85516200052481620004ac565b602087810151919650906001600160401b03808211156200054457600080fd5b818901915089601f8301126200055957600080fd5b8151818111156200056e576200056e620004c5565b8060051b604051601f19603f83011681018181108582111715620005965762000596620004c5565b60405291825284820192508381018501918c831115620005b557600080fd5b938501935b82851015620005de57620005ce85620004db565b84529385019392850192620005ba565b809950505050505050620005f560408701620004db565b92506200060560608701620004ed565b91506200061560808701620004db565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b818103818111156200033957634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60805160a05160c05160e0516141de6200070b600039600081816104ef015261174501526000818161059c01528181611cb1015261272501526000818161057601528181611b120152611f67015260008181610290015281816102e50152818161077a0152818161084c015281816108ed0152818161180701528181611a3201528181611e87015281816126bb015261291001526141de6000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c80638da5cb5b1161010f578063c4bffe2b116100a2578063dc0bd97111610071578063dc0bd97114610574578063e0351e131461059a578063eb521a4c146105c0578063f2fde38b146105d357600080fd5b8063c4bffe2b14610526578063c75eea9c1461053b578063cf7401f31461054e578063db6327dc1461056157600080fd5b8063b0f479a1116100de578063b0f479a1146104bc578063b7946580146104da578063bb98546b146104ed578063c0d786551461051357600080fd5b80638da5cb5b146103fa5780639a4575b914610418578063a7cd63b714610438578063af58d59f1461044d57600080fd5b806354c8a4f31161018757806378a010b21161015657806378a010b2146103b957806379ba5097146103cc5780637d54534e146103d45780638926f54f146103e757600080fd5b806354c8a4f31461036257806366320087146103755780636cfd1553146103885780636d3d1a581461039b57600080fd5b806321df0da7116101c357806321df0da71461028e578063240028e8146102d55780633907753714610322578063432a6ba31461034457600080fd5b806301ffc9a7146101f55780630a2fd4931461021d5780630a861f2a1461023d578063181f5a7714610252575b600080fd5b6102086102033660046132ba565b6105e6565b60405190151581526020015b60405180910390f35b61023061022b366004613319565b610642565b60405161021491906133a2565b61025061024b3660046133b5565b6106f2565b005b6102306040518060400160405280601a81526020017f4c6f636b52656c65617365546f6b656e506f6f6c20312e352e3000000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610214565b6102086102e33660046133fb565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b610335610330366004613418565b6108a3565b60405190518152602001610214565b60095473ffffffffffffffffffffffffffffffffffffffff166102b0565b6102506103703660046134a0565b6109a9565b61025061038336600461350c565b610a24565b6102506103963660046133fb565b610b00565b60085473ffffffffffffffffffffffffffffffffffffffff166102b0565b6102506103c7366004613538565b610b4f565b610250610cbe565b6102506103e23660046133fb565b610d8c565b6102086103f5366004613319565b610e0d565b60015473ffffffffffffffffffffffffffffffffffffffff166102b0565b61042b6104263660046135bb565b610e24565b60405161021491906135f6565b610440610ebe565b6040516102149190613656565b61046061045b366004613319565b610ecf565b604051610214919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff166102b0565b6102306104e8366004613319565b610fa4565b7f0000000000000000000000000000000000000000000000000000000000000000610208565b6102506105213660046133fb565b610fcf565b61052e6110aa565b60405161021491906136b0565b610460610549366004613319565b611162565b61025061055c366004613818565b611234565b61025061056f36600461385d565b6112bd565b7f00000000000000000000000000000000000000000000000000000000000000006102b0565b7f0000000000000000000000000000000000000000000000000000000000000000610208565b6102506105ce3660046133b5565b611743565b6102506105e13660046133fb565b61185f565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe1d4056600000000000000000000000000000000000000000000000000000000148061063c575061063c82611873565b92915050565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061066d9061389f565b80601f01602080910402602001604051908101604052809291908181526020018280546106999061389f565b80156106e65780601f106106bb576101008083540402835291602001916106e6565b820191906000526020600020905b8154815290600101906020018083116106c957829003601f168201915b50505050509050919050565b60095473ffffffffffffffffffffffffffffffffffffffff16331461074a576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa1580156107d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fa91906138f2565b1015610832576040517fbb55fd2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61087373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163383611957565b604051819033907fc2c3f06e49b9f15e7b4af9055e183b0d73362e033ad82a07dec9bf984017171990600090a350565b6040805160208101909152600081526108c36108be836139b6565b611a2b565b6109186108d660608401604085016133fb565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906060850135611957565b61092860608301604084016133fb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52846060013560405161098a91815260200190565b60405180910390a3506040805160208101909152606090910135815290565b6109b1611c5c565b610a1e84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250611caf92505050565b50505050565b610a2c611c5c565b6040517f0a861f2a0000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff831690630a861f2a90602401600060405180830381600087803b158015610a9457600080fd5b505af1158015610aa8573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff167f6fa7abcf1345d1d478e5ea0da6b5f26a90eadb0546ef15ed3833944fbfd1db6282604051610af491815260200190565b60405180910390a25050565b610b08611c5c565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b610b57611c5c565b610b6083610e0d565b610ba2576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610741565b67ffffffffffffffff831660009081526007602052604081206004018054610bc99061389f565b80601f0160208091040260200160405190810160405280929190818152602001828054610bf59061389f565b8015610c425780601f10610c1757610100808354040283529160200191610c42565b820191906000526020600020905b815481529060010190602001808311610c2557829003601f168201915b5050505067ffffffffffffffff8616600090815260076020526040902091925050600401610c71838583613afb565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610cb093929190613c16565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610d0f576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610d94611c5c565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d091749060200160405180910390a150565b600061063c600567ffffffffffffffff8416611e65565b6040805180820190915260608082526020820152610e49610e4483613c7a565b611e80565b6040516060830135815233907f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd600089060200160405180910390a26040518060400160405280610ea38460200160208101906104e89190613319565b81526040805160208181019092526000815291015292915050565b6060610eca600261204a565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff16151594820194909452600390910154808416606083015291909104909116608082015261063c90612057565b67ffffffffffffffff8116600090815260076020526040902060050180546060919061066d9061389f565b610fd7611c5c565b73ffffffffffffffffffffffffffffffffffffffff8116611024576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f1684910160405180910390a15050565b606060006110b8600561204a565b90506000815167ffffffffffffffff8111156110d6576110d66136f2565b6040519080825280602002602001820160405280156110ff578160200160208202803683370190505b50905060005b825181101561115b5782818151811061112057611120613d1c565b602002602001015182828151811061113a5761113a613d1c565b67ffffffffffffffff90921660209283029190910190910152600101611105565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff16151594820194909452600190910154808416606083015291909104909116608082015261063c90612057565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590611274575060015473ffffffffffffffffffffffffffffffffffffffff163314155b156112ad576040517f8e4a23d6000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b6112b8838383612109565b505050565b6112c5611c5c565b60005b818110156112b85760008383838181106112e4576112e4613d1c565b90506020028101906112f69190613d4b565b6112ff90613d89565b905061131481608001518260200151156121f3565b6113278160a001518260200151156121f3565b8060200151156116235780516113499060059067ffffffffffffffff1661232c565b61138e5780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610741565b60408101515115806113a35750606081015151155b156113da576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906115bb9082613e3d565b50606082015160058201906115d09082613e3d565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c295506116169493929190613f57565b60405180910390a161173a565b805161163b9060059067ffffffffffffffff16612338565b6116805780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610741565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906116e9600483018261326c565b6116f760058301600061326c565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b506001016112c8565b7f000000000000000000000000000000000000000000000000000000000000000061179a576040517fe93f8fa400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095473ffffffffffffffffffffffffffffffffffffffff1633146117ed576040517f8e4a23d6000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b61182f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333084612344565b604051819033907fc17cea59c2955cb181b03393209566960365771dbba9dc3d510180e7cb31208890600090a350565b611867611c5c565b611870816123a2565b50565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf00000000000000000000000000000000000000000000000000000000148061190657507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b8061063c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526112b89084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612466565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116911614611ac05760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610741565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611b6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b929190613ff0565b15611bc9576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611bd68160200151612572565b6000611be58260200151610642565b9050805160001480611c09575080805190602001208260a001518051906020012014155b15611c46578160a001516040517f24eb47e500000000000000000000000000000000000000000000000000000000815260040161074191906133a2565b611c5882602001518360600151612698565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611cad576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b7f0000000000000000000000000000000000000000000000000000000000000000611d06576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8251811015611d9c576000838281518110611d2657611d26613d1c565b60200260200101519050611d448160026126df90919063ffffffff16565b15611d935760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611d09565b5060005b81518110156112b8576000828281518110611dbd57611dbd613d1c565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611e015750611e5d565b611e0c600282612701565b15611e5b5760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611da0565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116911614611f155760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610741565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611fc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe79190613ff0565b1561201e576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61202b8160400151612723565b61203881602001516127a2565b611870816020015182606001516128f0565b60606000611e7983612934565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526120e582606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff16426120c9919061403c565b85608001516fffffffffffffffffffffffffffffffff1661298f565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b61211283610e0d565b612154576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610741565b61215f8260006121f3565b67ffffffffffffffff8316600090815260076020526040902061218290836129b9565b61218d8160006121f3565b67ffffffffffffffff831660009081526007602052604090206121b390600201826129b9565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b8383836040516121e69392919061404f565b60405180910390a1505050565b8151156122ba5781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff16101580612249575060408201516fffffffffffffffffffffffffffffffff16155b1561228257816040517f8020d12400000000000000000000000000000000000000000000000000000000815260040161074191906140d2565b8015611c58576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff161515806122f3575060208201516fffffffffffffffffffffffffffffffff1615155b15611c5857816040517fd68af9cc00000000000000000000000000000000000000000000000000000000815260040161074191906140d2565b6000611e798383612b5b565b6000611e798383612baa565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610a1e9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016119a9565b3373ffffffffffffffffffffffffffffffffffffffff8216036123f1576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006124c8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612c9d9092919063ffffffff16565b8051909150156112b857808060200190518101906124e69190613ff0565b6112b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610741565b61257b81610e0d565b6125bd576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610741565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa15801561263c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126609190613ff0565b611870576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b67ffffffffffffffff82166000908152600760205260409020611c5890600201827f0000000000000000000000000000000000000000000000000000000000000000612cac565b6000611e798373ffffffffffffffffffffffffffffffffffffffff8416612baa565b6000611e798373ffffffffffffffffffffffffffffffffffffffff8416612b5b565b7f0000000000000000000000000000000000000000000000000000000000000000156118705761275460028261302f565b611870576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610741565b6127ab81610e0d565b6127ed576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610741565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612866573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061288a919061410e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611870576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610741565b67ffffffffffffffff82166000908152600760205260409020611c5890827f0000000000000000000000000000000000000000000000000000000000000000612cac565b6060816000018054806020026020016040519081016040528092919081815260200182805480156106e657602002820191906000526020600020905b8154815260200190600101908083116129705750505050509050919050565b60006129ae8561299f848661412b565b6129a99087614142565b61305e565b90505b949350505050565b81546000906129e290700100000000000000000000000000000000900463ffffffff164261403c565b90508015612a845760018301548354612a2a916fffffffffffffffffffffffffffffffff8082169281169185917001000000000000000000000000000000009091041661298f565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612aaa916fffffffffffffffffffffffffffffffff908116911661305e565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c19906121e69084906140d2565b6000818152600183016020526040812054612ba25750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561063c565b50600061063c565b60008181526001830160205260408120548015612c93576000612bce60018361403c565b8554909150600090612be29060019061403c565b9050808214612c47576000866000018281548110612c0257612c02613d1c565b9060005260206000200154905080876000018481548110612c2557612c25613d1c565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612c5857612c58614155565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061063c565b600091505061063c565b60606129b18484600085613074565b825474010000000000000000000000000000000000000000900460ff161580612cd3575081155b15612cdd57505050565b825460018401546fffffffffffffffffffffffffffffffff80831692911690600090612d2390700100000000000000000000000000000000900463ffffffff164261403c565b90508015612de35781831115612d65576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001860154612d9f9083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1661298f565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b84821015612e9a5773ffffffffffffffffffffffffffffffffffffffff8416612e42576040517ff94ebcd10000000000000000000000000000000000000000000000000000000081526004810183905260248101869052604401610741565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff85166044820152606401610741565b84831015612fad5760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16906000908290612ede908261403c565b612ee8878a61403c565b612ef29190614142565b612efc9190614184565b905073ffffffffffffffffffffffffffffffffffffffff8616612f55576040517f15279c080000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610741565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff87166044820152606401610741565b612fb7858461403c565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515611e79565b600081831061306d5781611e79565b5090919050565b606082471015613106576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610741565b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161312f91906141bf565b60006040518083038185875af1925050503d806000811461316c576040519150601f19603f3d011682016040523d82523d6000602084013e613171565b606091505b50915091506131828783838761318d565b979650505050505050565b6060831561322357825160000361321c5773ffffffffffffffffffffffffffffffffffffffff85163b61321c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610741565b50816129b1565b6129b183838151156132385781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161074191906133a2565b5080546132789061389f565b6000825580601f10613288575050565b601f01602090049060005260206000209081019061187091905b808211156132b657600081556001016132a2565b5090565b6000602082840312156132cc57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611e7957600080fd5b803567ffffffffffffffff8116811461331457600080fd5b919050565b60006020828403121561332b57600080fd5b611e79826132fc565b60005b8381101561334f578181015183820152602001613337565b50506000910152565b60008151808452613370816020860160208601613334565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611e796020830184613358565b6000602082840312156133c757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461187057600080fd5b8035613314816133ce565b60006020828403121561340d57600080fd5b8135611e79816133ce565b60006020828403121561342a57600080fd5b813567ffffffffffffffff81111561344157600080fd5b82016101008185031215611e7957600080fd5b60008083601f84011261346657600080fd5b50813567ffffffffffffffff81111561347e57600080fd5b6020830191508360208260051b850101111561349957600080fd5b9250929050565b600080600080604085870312156134b657600080fd5b843567ffffffffffffffff808211156134ce57600080fd5b6134da88838901613454565b909650945060208701359150808211156134f357600080fd5b5061350087828801613454565b95989497509550505050565b6000806040838503121561351f57600080fd5b823561352a816133ce565b946020939093013593505050565b60008060006040848603121561354d57600080fd5b613556846132fc565b9250602084013567ffffffffffffffff8082111561357357600080fd5b818601915086601f83011261358757600080fd5b81358181111561359657600080fd5b8760208285010111156135a857600080fd5b6020830194508093505050509250925092565b6000602082840312156135cd57600080fd5b813567ffffffffffffffff8111156135e457600080fd5b820160a08185031215611e7957600080fd5b6020815260008251604060208401526136126060840182613358565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084830301604085015261364d8282613358565b95945050505050565b6020808252825182820181905260009190848201906040850190845b818110156136a457835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613672565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156136a457835167ffffffffffffffff16835292840192918401916001016136cc565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715613745576137456136f2565b60405290565b60405160c0810167ffffffffffffffff81118282101715613745576137456136f2565b801515811461187057600080fd5b80356133148161376e565b80356fffffffffffffffffffffffffffffffff8116811461331457600080fd5b6000606082840312156137b957600080fd5b6040516060810181811067ffffffffffffffff821117156137dc576137dc6136f2565b60405290508082356137ed8161376e565b81526137fb60208401613787565b602082015261380c60408401613787565b60408201525092915050565b600080600060e0848603121561382d57600080fd5b613836846132fc565b925061384585602086016137a7565b915061385485608086016137a7565b90509250925092565b6000806020838503121561387057600080fd5b823567ffffffffffffffff81111561388757600080fd5b61389385828601613454565b90969095509350505050565b600181811c908216806138b357607f821691505b6020821081036138ec577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60006020828403121561390457600080fd5b5051919050565b600082601f83011261391c57600080fd5b813567ffffffffffffffff80821115613937576139376136f2565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561397d5761397d6136f2565b8160405283815286602085880101111561399657600080fd5b836020870160208301376000602085830101528094505050505092915050565b600061010082360312156139c957600080fd5b6139d1613721565b823567ffffffffffffffff808211156139e957600080fd5b6139f53683870161390b565b8352613a03602086016132fc565b6020840152613a14604086016133f0565b604084015260608501356060840152613a2f608086016133f0565b608084015260a0850135915080821115613a4857600080fd5b613a543683870161390b565b60a084015260c0850135915080821115613a6d57600080fd5b613a793683870161390b565b60c084015260e0850135915080821115613a9257600080fd5b50613a9f3682860161390b565b60e08301525092915050565b601f8211156112b8576000816000526020600020601f850160051c81016020861015613ad45750805b601f850160051c820191505b81811015613af357828155600101613ae0565b505050505050565b67ffffffffffffffff831115613b1357613b136136f2565b613b2783613b21835461389f565b83613aab565b6000601f841160018114613b795760008515613b435750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613c0f565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613bc85786850135825560209485019460019092019101613ba8565b5086821015613c03577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b604081526000613c296040830186613358565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b600060a08236031215613c8c57600080fd5b60405160a0810167ffffffffffffffff8282108183111715613cb057613cb06136f2565b816040528435915080821115613cc557600080fd5b50613cd23682860161390b565b825250613ce1602084016132fc565b60208201526040830135613cf4816133ce565b6040820152606083810135908201526080830135613d11816133ce565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec1833603018112613d7f57600080fd5b9190910192915050565b60006101408236031215613d9c57600080fd5b613da461374b565b613dad836132fc565b8152613dbb6020840161377c565b6020820152604083013567ffffffffffffffff80821115613ddb57600080fd5b613de73683870161390b565b60408401526060850135915080821115613e0057600080fd5b50613e0d3682860161390b565b606083015250613e2036608085016137a7565b6080820152613e323660e085016137a7565b60a082015292915050565b815167ffffffffffffffff811115613e5757613e576136f2565b613e6b81613e65845461389f565b84613aab565b602080601f831160018114613ebe5760008415613e885750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555613af3565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015613f0b57888601518255948401946001909101908401613eec565b5085821015613f4757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff87168352806020840152613f7b81840187613358565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff9081166060870152908701511660808501529150613fb99050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e083015261364d565b60006020828403121561400257600080fd5b8151611e798161376e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561063c5761063c61400d565b67ffffffffffffffff8416815260e0810161409b60208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c08301526129b1565b6060810161063c82848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561412057600080fd5b8151611e79816133ce565b808202811582820484141761063c5761063c61400d565b8082018082111561063c5761063c61400d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000826141ba577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60008251613d7f81846020870161333456fea164736f6c6343000818000a", } var LockReleaseTokenPoolABI = LockReleaseTokenPoolMetaData.ABI @@ -2558,6 +2558,123 @@ func (_LockReleaseTokenPool *LockReleaseTokenPoolFilterer) ParseOwnershipTransfe return event, nil } +type LockReleaseTokenPoolRateLimitAdminSetIterator struct { + Event *LockReleaseTokenPoolRateLimitAdminSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LockReleaseTokenPoolRateLimitAdminSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LockReleaseTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LockReleaseTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LockReleaseTokenPoolRateLimitAdminSetIterator) Error() error { + return it.fail +} + +func (it *LockReleaseTokenPoolRateLimitAdminSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LockReleaseTokenPoolRateLimitAdminSet struct { + RateLimitAdmin common.Address + Raw types.Log +} + +func (_LockReleaseTokenPool *LockReleaseTokenPoolFilterer) FilterRateLimitAdminSet(opts *bind.FilterOpts) (*LockReleaseTokenPoolRateLimitAdminSetIterator, error) { + + logs, sub, err := _LockReleaseTokenPool.contract.FilterLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return &LockReleaseTokenPoolRateLimitAdminSetIterator{contract: _LockReleaseTokenPool.contract, event: "RateLimitAdminSet", logs: logs, sub: sub}, nil +} + +func (_LockReleaseTokenPool *LockReleaseTokenPoolFilterer) WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *LockReleaseTokenPoolRateLimitAdminSet) (event.Subscription, error) { + + logs, sub, err := _LockReleaseTokenPool.contract.WatchLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LockReleaseTokenPoolRateLimitAdminSet) + if err := _LockReleaseTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LockReleaseTokenPool *LockReleaseTokenPoolFilterer) ParseRateLimitAdminSet(log types.Log) (*LockReleaseTokenPoolRateLimitAdminSet, error) { + event := new(LockReleaseTokenPoolRateLimitAdminSet) + if err := _LockReleaseTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type LockReleaseTokenPoolReleasedIterator struct { Event *LockReleaseTokenPoolReleased @@ -3089,6 +3206,8 @@ func (_LockReleaseTokenPool *LockReleaseTokenPool) ParseLog(log types.Log) (gene return _LockReleaseTokenPool.ParseOwnershipTransferRequested(log) case _LockReleaseTokenPool.abi.Events["OwnershipTransferred"].ID: return _LockReleaseTokenPool.ParseOwnershipTransferred(log) + case _LockReleaseTokenPool.abi.Events["RateLimitAdminSet"].ID: + return _LockReleaseTokenPool.ParseRateLimitAdminSet(log) case _LockReleaseTokenPool.abi.Events["Released"].ID: return _LockReleaseTokenPool.ParseReleased(log) case _LockReleaseTokenPool.abi.Events["RemotePoolSet"].ID: @@ -3159,6 +3278,10 @@ func (LockReleaseTokenPoolOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (LockReleaseTokenPoolRateLimitAdminSet) Topic() common.Hash { + return common.HexToHash("0x44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d09174") +} + func (LockReleaseTokenPoolReleased) Topic() common.Hash { return common.HexToHash("0x2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52") } @@ -3328,6 +3451,12 @@ type LockReleaseTokenPoolInterface interface { ParseOwnershipTransferred(log types.Log) (*LockReleaseTokenPoolOwnershipTransferred, error) + FilterRateLimitAdminSet(opts *bind.FilterOpts) (*LockReleaseTokenPoolRateLimitAdminSetIterator, error) + + WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *LockReleaseTokenPoolRateLimitAdminSet) (event.Subscription, error) + + ParseRateLimitAdminSet(log types.Log) (*LockReleaseTokenPoolRateLimitAdminSet, error) + FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*LockReleaseTokenPoolReleasedIterator, error) WatchReleased(opts *bind.WatchOpts, sink chan<- *LockReleaseTokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, error) diff --git a/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go b/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go index 121135075db..315d75121b1 100644 --- a/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go +++ b/core/gethwrappers/ccip/generated/registry_module_owner_custom/registry_module_owner_custom.go @@ -31,8 +31,8 @@ var ( ) var RegistryModuleOwnerCustomMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AddressZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"CanOnlySelfRegister\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"administrator\",\"type\":\"address\"}],\"name\":\"AdministratorRegistered\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaGetCCIPAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60a060405234801561001057600080fd5b5060405161047e38038061047e83398101604081905261002f91610067565b6001600160a01b03811661005657604051639fabe1c160e01b815260040160405180910390fd5b6001600160a01b0316608052610097565b60006020828403121561007957600080fd5b81516001600160a01b038116811461009057600080fd5b9392505050565b6080516103cc6100b2600039600061024a01526103cc6000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063181f5a771461004657806396ea2f7a14610098578063ff12c354146100ad575b600080fd5b6100826040518060400160405280601f81526020017f52656769737472794d6f64756c654f776e6572437573746f6d20312e352e300081525081565b60405161008f91906102ef565b60405180910390f35b6100ab6100a636600461037e565b6100c0565b005b6100ab6100bb36600461037e565b61013b565b610138818273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561010f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013391906103a2565b61018a565b50565b610138818273ffffffffffffffffffffffffffffffffffffffff16638fd6a6ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561010f573d6000803e3d6000fd5b73ffffffffffffffffffffffffffffffffffffffff811633146101fd576040517fc454d18200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80831660048301528316602482015260440160405180910390fd5b6040517fe677ae3700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015282811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063e677ae3790604401600060405180830381600087803b15801561028e57600080fd5b505af11580156102a2573d6000803e3d6000fd5b505060405173ffffffffffffffffffffffffffffffffffffffff8085169350851691507f09590fb70af4b833346363965e043a9339e8c7d378b8a2b903c75c277faec4f990600090a35050565b60006020808352835180602085015260005b8181101561031d57858101830151858201604001528201610301565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461013857600080fd5b60006020828403121561039057600080fd5b813561039b8161035c565b9392505050565b6000602082840312156103b457600080fd5b815161039b8161035c56fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AddressZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"CanOnlySelfRegister\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"msgSender\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"RequiredRoleNotFound\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"administrator\",\"type\":\"address\"}],\"name\":\"AdministratorRegistered\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAccessControlDefaultAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaGetCCIPAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerAdminViaOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60a060405234801561001057600080fd5b5060405161064a38038061064a83398101604081905261002f91610067565b6001600160a01b03811661005657604051639fabe1c160e01b815260040160405180910390fd5b6001600160a01b0316608052610097565b60006020828403121561007957600080fd5b81516001600160a01b038116811461009057600080fd5b9392505050565b6080516105986100b260003960006103db01526105986000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063181f5a771461005157806369c0081e146100a357806396ea2f7a146100b8578063ff12c354146100cb575b600080fd5b61008d6040518060400160405280601f81526020017f52656769737472794d6f64756c654f776e6572437573746f6d20312e362e300081525081565b60405161009a9190610480565b60405180910390f35b6100b66100b136600461050f565b6100de565b005b6100b66100c636600461050f565b610255565b6100b66100d936600461050f565b6102d0565b60008173ffffffffffffffffffffffffffffffffffffffff1663a217fddf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014f9190610533565b6040517f91d148540000000000000000000000000000000000000000000000000000000081526004810182905233602482015290915073ffffffffffffffffffffffffffffffffffffffff8316906391d1485490604401602060405180830381865afa1580156101c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101e7919061054c565b610247576040517f86e0b3440000000000000000000000000000000000000000000000000000000081523360048201526024810182905273ffffffffffffffffffffffffffffffffffffffff831660448201526064015b60405180910390fd5b610251823361031f565b5050565b6102cd818273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c8919061056e565b61031f565b50565b6102cd818273ffffffffffffffffffffffffffffffffffffffff16638fd6a6ac6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102a4573d6000803e3d6000fd5b73ffffffffffffffffffffffffffffffffffffffff8116331461038e576040517fc454d18200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff80831660048301528316602482015260440161023e565b6040517fe677ae3700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015282811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063e677ae3790604401600060405180830381600087803b15801561041f57600080fd5b505af1158015610433573d6000803e3d6000fd5b505060405173ffffffffffffffffffffffffffffffffffffffff8085169350851691507f09590fb70af4b833346363965e043a9339e8c7d378b8a2b903c75c277faec4f990600090a35050565b60006020808352835180602085015260005b818110156104ae57858101830151858201604001528201610492565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146102cd57600080fd5b60006020828403121561052157600080fd5b813561052c816104ed565b9392505050565b60006020828403121561054557600080fd5b5051919050565b60006020828403121561055e57600080fd5b8151801515811461052c57600080fd5b60006020828403121561058057600080fd5b815161052c816104ed56fea164736f6c6343000818000a", } var RegistryModuleOwnerCustomABI = RegistryModuleOwnerCustomMetaData.ABI @@ -193,6 +193,18 @@ func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomCallerSession) TypeAn return _RegistryModuleOwnerCustom.Contract.TypeAndVersion(&_RegistryModuleOwnerCustom.CallOpts) } +func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomTransactor) RegisterAccessControlDefaultAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { + return _RegistryModuleOwnerCustom.contract.Transact(opts, "registerAccessControlDefaultAdmin", token) +} + +func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomSession) RegisterAccessControlDefaultAdmin(token common.Address) (*types.Transaction, error) { + return _RegistryModuleOwnerCustom.Contract.RegisterAccessControlDefaultAdmin(&_RegistryModuleOwnerCustom.TransactOpts, token) +} + +func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomTransactorSession) RegisterAccessControlDefaultAdmin(token common.Address) (*types.Transaction, error) { + return _RegistryModuleOwnerCustom.Contract.RegisterAccessControlDefaultAdmin(&_RegistryModuleOwnerCustom.TransactOpts, token) +} + func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustomTransactor) RegisterAdminViaGetCCIPAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { return _RegistryModuleOwnerCustom.contract.Transact(opts, "registerAdminViaGetCCIPAdmin", token) } @@ -374,6 +386,8 @@ func (_RegistryModuleOwnerCustom *RegistryModuleOwnerCustom) Address() common.Ad type RegistryModuleOwnerCustomInterface interface { TypeAndVersion(opts *bind.CallOpts) (string, error) + RegisterAccessControlDefaultAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) + RegisterAdminViaGetCCIPAdmin(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) RegisterAdminViaOwner(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) diff --git a/core/gethwrappers/ccip/generated/token_pool/token_pool.go b/core/gethwrappers/ccip/generated/token_pool/token_pool.go index 075ee6f8ba2..3465ff76fe0 100644 --- a/core/gethwrappers/ccip/generated/token_pool/token_pool.go +++ b/core/gethwrappers/ccip/generated/token_pool/token_pool.go @@ -82,7 +82,7 @@ type TokenPoolChainUpdate struct { } var TokenPoolMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"lockOrBurnOut\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"RateLimitAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"lockOrBurnOut\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } var TokenPoolABI = TokenPoolMetaData.ABI @@ -2025,6 +2025,123 @@ func (_TokenPool *TokenPoolFilterer) ParseOwnershipTransferred(log types.Log) (* return event, nil } +type TokenPoolRateLimitAdminSetIterator struct { + Event *TokenPoolRateLimitAdminSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *TokenPoolRateLimitAdminSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(TokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(TokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *TokenPoolRateLimitAdminSetIterator) Error() error { + return it.fail +} + +func (it *TokenPoolRateLimitAdminSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type TokenPoolRateLimitAdminSet struct { + RateLimitAdmin common.Address + Raw types.Log +} + +func (_TokenPool *TokenPoolFilterer) FilterRateLimitAdminSet(opts *bind.FilterOpts) (*TokenPoolRateLimitAdminSetIterator, error) { + + logs, sub, err := _TokenPool.contract.FilterLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return &TokenPoolRateLimitAdminSetIterator{contract: _TokenPool.contract, event: "RateLimitAdminSet", logs: logs, sub: sub}, nil +} + +func (_TokenPool *TokenPoolFilterer) WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *TokenPoolRateLimitAdminSet) (event.Subscription, error) { + + logs, sub, err := _TokenPool.contract.WatchLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(TokenPoolRateLimitAdminSet) + if err := _TokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_TokenPool *TokenPoolFilterer) ParseRateLimitAdminSet(log types.Log) (*TokenPoolRateLimitAdminSet, error) { + event := new(TokenPoolRateLimitAdminSet) + if err := _TokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type TokenPoolReleasedIterator struct { Event *TokenPoolReleased @@ -2433,6 +2550,8 @@ func (_TokenPool *TokenPool) ParseLog(log types.Log) (generated.AbigenLog, error return _TokenPool.ParseOwnershipTransferRequested(log) case _TokenPool.abi.Events["OwnershipTransferred"].ID: return _TokenPool.ParseOwnershipTransferred(log) + case _TokenPool.abi.Events["RateLimitAdminSet"].ID: + return _TokenPool.ParseRateLimitAdminSet(log) case _TokenPool.abi.Events["Released"].ID: return _TokenPool.ParseReleased(log) case _TokenPool.abi.Events["RemotePoolSet"].ID: @@ -2489,6 +2608,10 @@ func (TokenPoolOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (TokenPoolRateLimitAdminSet) Topic() common.Hash { + return common.HexToHash("0x44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d09174") +} + func (TokenPoolReleased) Topic() common.Hash { return common.HexToHash("0x2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52") } @@ -2622,6 +2745,12 @@ type TokenPoolInterface interface { ParseOwnershipTransferred(log types.Log) (*TokenPoolOwnershipTransferred, error) + FilterRateLimitAdminSet(opts *bind.FilterOpts) (*TokenPoolRateLimitAdminSetIterator, error) + + WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *TokenPoolRateLimitAdminSet) (event.Subscription, error) + + ParseRateLimitAdminSet(log types.Log) (*TokenPoolRateLimitAdminSet, error) + FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*TokenPoolReleasedIterator, error) WatchReleased(opts *bind.WatchOpts, sink chan<- *TokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, error) diff --git a/core/gethwrappers/ccip/generated/usdc_token_pool/usdc_token_pool.go b/core/gethwrappers/ccip/generated/usdc_token_pool/usdc_token_pool.go index 8998b972aef..1d6b173b628 100644 --- a/core/gethwrappers/ccip/generated/usdc_token_pool/usdc_token_pool.go +++ b/core/gethwrappers/ccip/generated/usdc_token_pool/usdc_token_pool.go @@ -95,8 +95,8 @@ type USDCTokenPoolDomainUpdate struct { } var USDCTokenPoolMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractITokenMessenger\",\"name\":\"tokenMessenger\",\"type\":\"address\"},{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"expected\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"got\",\"type\":\"uint32\"}],\"name\":\"InvalidDestinationDomain\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structUSDCTokenPool.DomainUpdate\",\"name\":\"domain\",\"type\":\"tuple\"}],\"name\":\"InvalidDomain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"}],\"name\":\"InvalidMessageVersion\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"expected\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"got\",\"type\":\"uint64\"}],\"name\":\"InvalidNonce\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"}],\"name\":\"InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"expected\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"got\",\"type\":\"uint32\"}],\"name\":\"InvalidSourceDomain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"}],\"name\":\"InvalidTokenMessengerVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"domain\",\"type\":\"uint64\"}],\"name\":\"UnknownDomain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnlockingUSDCFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"tokenMessenger\",\"type\":\"address\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structUSDCTokenPool.DomainUpdate[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"DomainsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"SUPPORTED_USDC_VERSION\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"getDomain\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structUSDCTokenPool.Domain\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_localDomainIdentifier\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_messageTransmitter\",\"outputs\":[{\"internalType\":\"contractIMessageTransmitter\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_tokenMessenger\",\"outputs\":[{\"internalType\":\"contractITokenMessenger\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structUSDCTokenPool.DomainUpdate[]\",\"name\":\"domains\",\"type\":\"tuple[]\"}],\"name\":\"setDomains\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x6101406040523480156200001257600080fd5b50604051620051d3380380620051d3833981016040819052620000359162000ae9565b83838383336000816200005b57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008e576200008e81620003e9565b50506001600160a01b0384161580620000ae57506001600160a01b038116155b80620000c157506001600160a01b038216155b15620000e0576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c052620001335760408051600081526020810190915262000133908462000463565b5050506001600160a01b038616905062000160576040516306b7c75960e31b815260040160405180910390fd5b6000856001600160a01b0316632c1219216040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001c7919062000c0f565b90506000816001600160a01b03166354fd4d506040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200020a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000230919062000c36565b905063ffffffff81161562000265576040516334697c6b60e11b815263ffffffff821660048201526024015b60405180910390fd5b6000876001600160a01b0316639cdbb1816040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002a6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002cc919062000c36565b905063ffffffff811615620002fd576040516316ba39c560e31b815263ffffffff821660048201526024016200025c565b6001600160a01b0380891660e05283166101008190526040805163234d8e3d60e21b81529051638d3638f4916004808201926020929091908290030181865afa1580156200034f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000375919062000c36565b63ffffffff166101205260e0516080516200039f916001600160a01b0390911690600019620005c0565b6040516001600160a01b03891681527f2e902d38f15b233cbb63711add0fca4545334d3a169d60c0a616494d7eea95449060200160405180910390a1505050505050505062000d83565b336001600160a01b038216036200041357604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c05162000484576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200050f576000838281518110620004a857620004a862000c5e565b60209081029190910101519050620004c2600282620006a6565b1562000505576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b5060010162000487565b5060005b8151811015620005bb57600082828151811062000534576200053462000c5e565b6020026020010151905060006001600160a01b0316816001600160a01b031603620005605750620005b2565b6200056d600282620006c6565b15620005b0576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000513565b505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801562000612573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000638919062000c74565b62000644919062000ca4565b604080516001600160a01b038616602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152919250620006a091869190620006dd16565b50505050565b6000620006bd836001600160a01b038416620007ae565b90505b92915050565b6000620006bd836001600160a01b038416620008b2565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200072c906001600160a01b03851690849062000904565b805190915015620005bb57808060200190518101906200074d919062000cba565b620005bb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016200025c565b60008181526001830160205260408120548015620008a7576000620007d560018362000cde565b8554909150600090620007eb9060019062000cde565b9050808214620008575760008660000182815481106200080f576200080f62000c5e565b906000526020600020015490508087600001848154811062000835576200083562000c5e565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806200086b576200086b62000cf4565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050620006c0565b6000915050620006c0565b6000818152600183016020526040812054620008fb57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620006c0565b506000620006c0565b60606200091584846000856200091d565b949350505050565b606082471015620009805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016200025c565b600080866001600160a01b031685876040516200099e919062000d30565b60006040518083038185875af1925050503d8060008114620009dd576040519150601f19603f3d011682016040523d82523d6000602084013e620009e2565b606091505b509092509050620009f68783838762000a01565b979650505050505050565b6060831562000a7557825160000362000a6d576001600160a01b0385163b62000a6d5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200025c565b508162000915565b62000915838381511562000a8c5781518083602001fd5b8060405162461bcd60e51b81526004016200025c919062000d4e565b6001600160a01b038116811462000abe57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b805162000ae48162000aa8565b919050565b600080600080600060a0868803121562000b0257600080fd5b855162000b0f8162000aa8565b8095505060208087015162000b248162000aa8565b60408801519095506001600160401b038082111562000b4257600080fd5b818901915089601f83011262000b5757600080fd5b81518181111562000b6c5762000b6c62000ac1565b8060051b604051601f19603f8301168101818110858211171562000b945762000b9462000ac1565b60405291825284820192508381018501918c83111562000bb357600080fd5b938501935b8285101562000bdc5762000bcc8562000ad7565b8452938501939285019262000bb8565b80985050505050505062000bf36060870162000ad7565b915062000c036080870162000ad7565b90509295509295909350565b60006020828403121562000c2257600080fd5b815162000c2f8162000aa8565b9392505050565b60006020828403121562000c4957600080fd5b815163ffffffff8116811462000c2f57600080fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121562000c8757600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115620006c057620006c062000c8e565b60006020828403121562000ccd57600080fd5b8151801515811462000c2f57600080fd5b81810381811115620006c057620006c062000c8e565b634e487b7160e01b600052603160045260246000fd5b60005b8381101562000d2757818101518382015260200162000d0d565b50506000910152565b6000825162000d4481846020870162000d0a565b9190910192915050565b602081526000825180602084015262000d6f81604085016020870162000d0a565b601f01601f19169190910160400192915050565b60805160a05160c05160e051610100516101205161439762000e3c60003960008181610382015281816111a201528181611deb0152611e490152600081816106760152610a7801526000818161035b01526110b801526000818161063a01528181611ee601526127f001526000818161057601528181611be9015261219c01526000818161028f015281816102e40152818161108201528181611b09015281816120bc0152818161278601526129db01526143976000f3fe608060405234801561001057600080fd5b50600436106101ef5760003560e01c80639a4575b91161010f578063c75eea9c116100a2578063dfadfa3511610071578063dfadfa351461059a578063e0351e1314610638578063f2fde38b1461065e578063fbf84dd71461067157600080fd5b8063c75eea9c1461053b578063cf7401f31461054e578063db6327dc14610561578063dc0bd9711461057457600080fd5b8063b0f479a1116100de578063b0f479a1146104e2578063b794658014610500578063c0d7865514610513578063c4bffe2b1461052657600080fd5b80639a4575b9146104365780639fdf13ff14610456578063a7cd63b71461045e578063af58d59f1461047357600080fd5b80636155cda01161018757806379ba50971161015657806379ba5097146103ea5780637d54534e146103f25780638926f54f146104055780638da5cb5b1461041857600080fd5b80636155cda0146103565780636b716b0d1461037d5780636d3d1a58146103b957806378a010b2146103d757600080fd5b806321df0da7116101c357806321df0da71461028d578063240028e8146102d4578063390775371461032157806354c8a4f31461034357600080fd5b806241d3c1146101f457806301ffc9a7146102095780630a2fd49314610231578063181f5a7714610251575b600080fd5b61020761020236600461317e565b610698565b005b61021c6102173660046131f3565b610835565b60405190151581526020015b60405180910390f35b61024461023f36600461325b565b61091a565b60405161022891906132dc565b6102446040518060400160405280601381526020017f55534443546f6b656e506f6f6c20312e352e300000000000000000000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610228565b61021c6102e236600461331c565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b61033461032f366004613339565b6109ca565b60405190518152602001610228565b6102076103513660046133c1565b610bb7565b6102af7f000000000000000000000000000000000000000000000000000000000000000081565b6103a47f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610228565b60085473ffffffffffffffffffffffffffffffffffffffff166102af565b6102076103e536600461342d565b610c32565b610207610da1565b61020761040036600461331c565b610e6f565b61021c61041336600461325b565b610ebe565b60015473ffffffffffffffffffffffffffffffffffffffff166102af565b6104496104443660046134b2565b610ed5565b60405161022891906134ed565b6103a4600081565b61046661121d565b604051610228919061354d565b61048661048136600461325b565b61122e565b604051610228919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff166102af565b61024461050e36600461325b565b611303565b61020761052136600461331c565b61132e565b61052e611402565b60405161022891906135a7565b61048661054936600461325b565b6114ba565b61020761055c366004613732565b61158c565b61020761056f366004613779565b611615565b7f00000000000000000000000000000000000000000000000000000000000000006102af565b61060e6105a836600461325b565b60408051606080820183526000808352602080840182905292840181905267ffffffffffffffff949094168452600982529282902082519384018352805484526001015463ffffffff811691840191909152640100000000900460ff1615159082015290565b604080518251815260208084015163ffffffff169082015291810151151590820152606001610228565b7f000000000000000000000000000000000000000000000000000000000000000061021c565b61020761066c36600461331c565b611a9b565b6102af7f000000000000000000000000000000000000000000000000000000000000000081565b6106a0611aaf565b60005b818110156107f75760008383838181106106bf576106bf6137bb565b9050608002018036038101906106d591906137fe565b805190915015806106f25750604081015167ffffffffffffffff16155b1561076157604080517fa087bd2900000000000000000000000000000000000000000000000000000000815282516004820152602083015163ffffffff1660248201529082015167ffffffffffffffff1660448201526060820151151560648201526084015b60405180910390fd5b60408051606080820183528351825260208085015163ffffffff9081168285019081529286015115158486019081529585015167ffffffffffffffff166000908152600990925293902091518255516001918201805494511515640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009095169190931617929092179055016106a3565b507f1889010d2535a0ab1643678d1da87fbbe8b87b2f585b47ddb72ec622aef9ee568282604051610829929190613878565b60405180910390a15050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806108c857507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b8061091457507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff81166000908152600760205260409020600401805460609190610945906138ff565b80601f0160208091040260200160405190810160405280929190818152602001828054610971906138ff565b80156109be5780601f10610993576101008083540402835291602001916109be565b820191906000526020600020905b8154815290600101906020018083116109a157829003601f168201915b50505050509050919050565b6040805160208101909152600081526109ea6109e5836139fd565b611b02565b60006109f960c0840184613af2565b810190610a069190613b57565b90506000610a1760e0850185613af2565b810190610a249190613b96565b9050610a34816000015183611d33565b805160208201516040517f57ecfd2800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016926357ecfd2892610aab92600401613c27565b6020604051808303816000875af1158015610aca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aee9190613c4c565b610b24576040517fbf969f2200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b34606085016040860161331c565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f08660600135604051610b9691815260200190565b60405180910390a35050604080516020810190915260609092013582525090565b610bbf611aaf565b610c2c84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250611ee492505050565b50505050565b610c3a611aaf565b610c4383610ebe565b610c85576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610758565b67ffffffffffffffff831660009081526007602052604081206004018054610cac906138ff565b80601f0160208091040260200160405190810160405280929190818152602001828054610cd8906138ff565b8015610d255780601f10610cfa57610100808354040283529160200191610d25565b820191906000526020600020905b815481529060010190602001808311610d0857829003601f168201915b5050505067ffffffffffffffff8616600090815260076020526040902091925050600401610d54838583613cb1565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610d9393929190613e15565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610df2576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e77611aaf565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000610914600567ffffffffffffffff841661209a565b6040805180820190915260608082526020820152610efa610ef583613e45565b6120b5565b6000600981610f0f604086016020870161325b565b67ffffffffffffffff168152602080820192909252604090810160002081516060810183528154815260019091015463ffffffff81169382019390935264010000000090920460ff161515908201819052909150610fb657610f77604084016020850161325b565b6040517fd201c48a00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610758565b610fc08380613af2565b905060201461100757610fd38380613af2565b6040517fa3c8cf09000000000000000000000000000000000000000000000000000000008152600401610758929190613ee9565b60006110138480613af2565b8101906110209190613efd565b602083015183516040517ff856ddb60000000000000000000000000000000000000000000000000000000081526060880135600482015263ffffffff90921660248301526044820183905273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116606484015260848301919091529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063f856ddb69060a4016020604051808303816000875af1158015611101573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111259190613f16565b6040516060870135815290915033907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a2604051806040016040528061118287602001602081019061050e919061325b565b815260408051808201825267ffffffffffffffff851680825263ffffffff7f00000000000000000000000000000000000000000000000000000000000000008116602093840190815284518085019390935251169281019290925290910190606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905295945050505050565b6060611229600261227f565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526109149061228c565b67ffffffffffffffff81166000908152600760205260409020600501805460609190610945906138ff565b611336611aaf565b73ffffffffffffffffffffffffffffffffffffffff8116611383576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f16849101610829565b60606000611410600561227f565b90506000815167ffffffffffffffff81111561142e5761142e6135e9565b604051908082528060200260200182016040528015611457578160200160208202803683370190505b50905060005b82518110156114b357828181518110611478576114786137bb565b6020026020010151828281518110611492576114926137bb565b67ffffffffffffffff9092166020928302919091019091015260010161145d565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526109149061228c565b60085473ffffffffffffffffffffffffffffffffffffffff1633148015906115cc575060015473ffffffffffffffffffffffffffffffffffffffff163314155b15611605576040517f8e4a23d6000000000000000000000000000000000000000000000000000000008152336004820152602401610758565b61161083838361233e565b505050565b61161d611aaf565b60005b8181101561161057600083838381811061163c5761163c6137bb565b905060200281019061164e9190613f33565b61165790613f71565b905061166c8160800151826020015115612428565b61167f8160a00151826020015115612428565b80602001511561197b5780516116a19060059067ffffffffffffffff16612561565b6116e65780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610758565b60408101515115806116fb5750606081015151155b15611732576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906119139082614025565b50606082015160058201906119289082614025565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c2955061196e949392919061413f565b60405180910390a1611a92565b80516119939060059067ffffffffffffffff1661256d565b6119d85780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610758565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff00000000000000000000000000000000000000000090811682556001820183905560028201805490911690556003810182905590611a416004830182613130565b611a4f600583016000613130565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611620565b611aa3611aaf565b611aac81612579565b50565b60015473ffffffffffffffffffffffffffffffffffffffff163314611b00576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116911614611b975760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610758565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611c45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c699190613c4c565b15611ca0576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611cad816020015161263d565b6000611cbc826020015161091a565b9050805160001480611ce0575080805190602001208260a001518051906020012014155b15611d1d578160a001516040517f24eb47e500000000000000000000000000000000000000000000000000000000815260040161075891906132dc565b611d2f82602001518360600151612763565b5050565b600482015163ffffffff811615611d7e576040517f68d2f8d600000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401610758565b6008830151600c8401516014850151602085015163ffffffff808516911614611de95760208501516040517fe366a11700000000000000000000000000000000000000000000000000000000815263ffffffff91821660048201529084166024820152604401610758565b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168263ffffffff1614611e7e576040517f77e4802600000000000000000000000000000000000000000000000000000000815263ffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015283166024820152604401610758565b845167ffffffffffffffff828116911614611edc5784516040517ff917ffea00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff91821660048201529082166024820152604401610758565b505050505050565b7f0000000000000000000000000000000000000000000000000000000000000000611f3b576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8251811015611fd1576000838281518110611f5b57611f5b6137bb565b60200260200101519050611f798160026127aa90919063ffffffff16565b15611fc85760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611f3e565b5060005b8151811015611610576000828281518110611ff257611ff26137bb565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036120365750612092565b6120416002826127cc565b156120905760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101611fd5565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161461214a5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610758565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa1580156121f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221c9190613c4c565b15612253576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61226081604001516127ee565b61226d816020015161286d565b611aac816020015182606001516129bb565b606060006120ae836129ff565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915261231a82606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff16426122fe9190614207565b85608001516fffffffffffffffffffffffffffffffff16612a5a565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b61234783610ebe565b612389576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610758565b612394826000612428565b67ffffffffffffffff831660009081526007602052604090206123b79083612a84565b6123c2816000612428565b67ffffffffffffffff831660009081526007602052604090206123e89060020182612a84565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b83838360405161241b9392919061421a565b60405180910390a1505050565b8151156124ef5781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff1610158061247e575060408201516fffffffffffffffffffffffffffffffff16155b156124b757816040517f8020d124000000000000000000000000000000000000000000000000000000008152600401610758919061429d565b8015611d2f576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff16151580612528575060208201516fffffffffffffffffffffffffffffffff1615155b15611d2f57816040517fd68af9cc000000000000000000000000000000000000000000000000000000008152600401610758919061429d565b60006120ae8383612c26565b60006120ae8383612c75565b3373ffffffffffffffffffffffffffffffffffffffff8216036125c8576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61264681610ebe565b612688576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610758565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015612707573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061272b9190613c4c565b611aac576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610758565b67ffffffffffffffff82166000908152600760205260409020611d2f90600201827f0000000000000000000000000000000000000000000000000000000000000000612d68565b60006120ae8373ffffffffffffffffffffffffffffffffffffffff8416612c75565b60006120ae8373ffffffffffffffffffffffffffffffffffffffff8416612c26565b7f000000000000000000000000000000000000000000000000000000000000000015611aac5761281f6002826130eb565b611aac576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610758565b61287681610ebe565b6128b8576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610758565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612931573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061295591906142d9565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611aac576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610758565b67ffffffffffffffff82166000908152600760205260409020611d2f90827f0000000000000000000000000000000000000000000000000000000000000000612d68565b6060816000018054806020026020016040519081016040528092919081815260200182805480156109be57602002820191906000526020600020905b815481526020019060010190808311612a3b5750505050509050919050565b6000612a7985612a6a84866142f6565b612a74908761430d565b61311a565b90505b949350505050565b8154600090612aad90700100000000000000000000000000000000900463ffffffff1642614207565b90508015612b4f5760018301548354612af5916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612a5a565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612b75916fffffffffffffffffffffffffffffffff908116911661311a565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c199061241b90849061429d565b6000818152600183016020526040812054612c6d57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610914565b506000610914565b60008181526001830160205260408120548015612d5e576000612c99600183614207565b8554909150600090612cad90600190614207565b9050808214612d12576000866000018281548110612ccd57612ccd6137bb565b9060005260206000200154905080876000018481548110612cf057612cf06137bb565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612d2357612d23614320565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610914565b6000915050610914565b825474010000000000000000000000000000000000000000900460ff161580612d8f575081155b15612d9957505050565b825460018401546fffffffffffffffffffffffffffffffff80831692911690600090612ddf90700100000000000000000000000000000000900463ffffffff1642614207565b90508015612e9f5781831115612e21576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001860154612e5b9083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612a5a565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b84821015612f565773ffffffffffffffffffffffffffffffffffffffff8416612efe576040517ff94ebcd10000000000000000000000000000000000000000000000000000000081526004810183905260248101869052604401610758565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff85166044820152606401610758565b848310156130695760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16906000908290612f9a9082614207565b612fa4878a614207565b612fae919061430d565b612fb8919061434f565b905073ffffffffffffffffffffffffffffffffffffffff8616613011576040517f15279c080000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610758565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff87166044820152606401610758565b6130738584614207565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415156120ae565b600081831061312957816120ae565b5090919050565b50805461313c906138ff565b6000825580601f1061314c575050565b601f016020900490600052602060002090810190611aac91905b8082111561317a5760008155600101613166565b5090565b6000806020838503121561319157600080fd5b823567ffffffffffffffff808211156131a957600080fd5b818501915085601f8301126131bd57600080fd5b8135818111156131cc57600080fd5b8660208260071b85010111156131e157600080fd5b60209290920196919550909350505050565b60006020828403121561320557600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146120ae57600080fd5b67ffffffffffffffff81168114611aac57600080fd5b803561325681613235565b919050565b60006020828403121561326d57600080fd5b81356120ae81613235565b6000815180845260005b8181101561329e57602081850181015186830182015201613282565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006120ae6020830184613278565b73ffffffffffffffffffffffffffffffffffffffff81168114611aac57600080fd5b8035613256816132ef565b60006020828403121561332e57600080fd5b81356120ae816132ef565b60006020828403121561334b57600080fd5b813567ffffffffffffffff81111561336257600080fd5b820161010081850312156120ae57600080fd5b60008083601f84011261338757600080fd5b50813567ffffffffffffffff81111561339f57600080fd5b6020830191508360208260051b85010111156133ba57600080fd5b9250929050565b600080600080604085870312156133d757600080fd5b843567ffffffffffffffff808211156133ef57600080fd5b6133fb88838901613375565b9096509450602087013591508082111561341457600080fd5b5061342187828801613375565b95989497509550505050565b60008060006040848603121561344257600080fd5b833561344d81613235565b9250602084013567ffffffffffffffff8082111561346a57600080fd5b818601915086601f83011261347e57600080fd5b81358181111561348d57600080fd5b87602082850101111561349f57600080fd5b6020830194508093505050509250925092565b6000602082840312156134c457600080fd5b813567ffffffffffffffff8111156134db57600080fd5b820160a081850312156120ae57600080fd5b6020815260008251604060208401526135096060840182613278565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08483030160408501526135448282613278565b95945050505050565b6020808252825182820181905260009190848201906040850190845b8181101561359b57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613569565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561359b57835167ffffffffffffffff16835292840192918401916001016135c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff8111828210171561363c5761363c6135e9565b60405290565b6040805190810167ffffffffffffffff8111828210171561363c5761363c6135e9565b60405160c0810167ffffffffffffffff8111828210171561363c5761363c6135e9565b8015158114611aac57600080fd5b803561325681613688565b80356fffffffffffffffffffffffffffffffff8116811461325657600080fd5b6000606082840312156136d357600080fd5b6040516060810181811067ffffffffffffffff821117156136f6576136f66135e9565b604052905080823561370781613688565b8152613715602084016136a1565b6020820152613726604084016136a1565b60408201525092915050565b600080600060e0848603121561374757600080fd5b833561375281613235565b925061376185602086016136c1565b915061377085608086016136c1565b90509250925092565b6000806020838503121561378c57600080fd5b823567ffffffffffffffff8111156137a357600080fd5b6137af85828601613375565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b803563ffffffff8116811461325657600080fd5b60006080828403121561381057600080fd5b6040516080810181811067ffffffffffffffff82111715613833576138336135e9565b60405282358152613846602084016137ea565b6020820152604083013561385981613235565b6040820152606083013561386c81613688565b60608201529392505050565b6020808252818101839052600090604080840186845b878110156138f2578135835263ffffffff6138aa8684016137ea565b1685840152838201356138bc81613235565b67ffffffffffffffff16838501526060828101356138d981613688565b151590840152608092830192919091019060010161388e565b5090979650505050505050565b600181811c9082168061391357607f821691505b60208210810361394c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261396357600080fd5b813567ffffffffffffffff8082111561397e5761397e6135e9565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156139c4576139c46135e9565b816040528381528660208588010111156139dd57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006101008236031215613a1057600080fd5b613a18613618565b823567ffffffffffffffff80821115613a3057600080fd5b613a3c36838701613952565b8352613a4a6020860161324b565b6020840152613a5b60408601613311565b604084015260608501356060840152613a7660808601613311565b608084015260a0850135915080821115613a8f57600080fd5b613a9b36838701613952565b60a084015260c0850135915080821115613ab457600080fd5b613ac036838701613952565b60c084015260e0850135915080821115613ad957600080fd5b50613ae636828601613952565b60e08301525092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613b2757600080fd5b83018035915067ffffffffffffffff821115613b4257600080fd5b6020019150368190038213156133ba57600080fd5b600060408284031215613b6957600080fd5b613b71613642565b8235613b7c81613235565b8152613b8a602084016137ea565b60208201529392505050565b600060208284031215613ba857600080fd5b813567ffffffffffffffff80821115613bc057600080fd5b9083019060408286031215613bd457600080fd5b613bdc613642565b823582811115613beb57600080fd5b613bf787828601613952565b825250602083013582811115613c0c57600080fd5b613c1887828601613952565b60208301525095945050505050565b604081526000613c3a6040830185613278565b82810360208401526135448185613278565b600060208284031215613c5e57600080fd5b81516120ae81613688565b601f821115611610576000816000526020600020601f850160051c81016020861015613c925750805b601f850160051c820191505b81811015611edc57828155600101613c9e565b67ffffffffffffffff831115613cc957613cc96135e9565b613cdd83613cd783546138ff565b83613c69565b6000601f841160018114613d2f5760008515613cf95750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613dc5565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613d7e5786850135825560209485019460019092019101613d5e565b5086821015613db9577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b604081526000613e286040830186613278565b8281036020840152613e3b818587613dcc565b9695505050505050565b600060a08236031215613e5757600080fd5b60405160a0810167ffffffffffffffff8282108183111715613e7b57613e7b6135e9565b816040528435915080821115613e9057600080fd5b50613e9d36828601613952565b8252506020830135613eae81613235565b60208201526040830135613ec1816132ef565b6040820152606083810135908201526080830135613ede816132ef565b608082015292915050565b602081526000612a7c602083018486613dcc565b600060208284031215613f0f57600080fd5b5035919050565b600060208284031215613f2857600080fd5b81516120ae81613235565b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec1833603018112613f6757600080fd5b9190910192915050565b60006101408236031215613f8457600080fd5b613f8c613665565b613f958361324b565b8152613fa360208401613696565b6020820152604083013567ffffffffffffffff80821115613fc357600080fd5b613fcf36838701613952565b60408401526060850135915080821115613fe857600080fd5b50613ff536828601613952565b60608301525061400836608085016136c1565b608082015261401a3660e085016136c1565b60a082015292915050565b815167ffffffffffffffff81111561403f5761403f6135e9565b6140538161404d84546138ff565b84613c69565b602080601f8311600181146140a657600084156140705750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555611edc565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156140f3578886015182559484019460019091019084016140d4565b508582101561412f57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff8716835280602084015261416381840187613278565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506141a19050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152613544565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115610914576109146141d8565b67ffffffffffffffff8416815260e0810161426660208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612a7c565b6060810161091482848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b6000602082840312156142eb57600080fd5b81516120ae816132ef565b8082028115828204841417610914576109146141d8565b80820180821115610914576109146141d8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600082614385577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"contractITokenMessenger\",\"name\":\"tokenMessenger\",\"type\":\"address\"},{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowlist\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"AggregateValueMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"}],\"name\":\"AggregateValueRateLimitReached\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AllowListNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BucketOverfilled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerIsNotARampOnRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotTransferToSelf\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"DisabledNonZeroRateLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"expected\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"got\",\"type\":\"uint32\"}],\"name\":\"InvalidDestinationDomain\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structUSDCTokenPool.DomainUpdate\",\"name\":\"domain\",\"type\":\"tuple\"}],\"name\":\"InvalidDomain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"}],\"name\":\"InvalidMessageVersion\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"expected\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"got\",\"type\":\"uint64\"}],\"name\":\"InvalidNonce\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"rateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"InvalidRateLimitRate\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"}],\"name\":\"InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"expected\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"got\",\"type\":\"uint32\"}],\"name\":\"InvalidSourceDomain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidSourcePoolAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"}],\"name\":\"InvalidTokenMessengerVersion\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeProposedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"NonExistentChain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OwnerCannotBeZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RateLimitMustBeDisabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"capacity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenMaxCapacityExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minWaitInSeconds\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"TokenRateLimitReached\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"domain\",\"type\":\"uint64\"}],\"name\":\"UnknownDomain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnlockingUSDCFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListAdd\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AllowListRemove\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Burned\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remoteToken\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"name\":\"ChainConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"ChainRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"structRateLimiter.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"tokenMessenger\",\"type\":\"address\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structUSDCTokenPool.DomainUpdate[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"DomainsSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Locked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Minted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"RateLimitAdminSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Released\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"previousPoolAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"RemotePoolSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldRouter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"RouterUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokens\",\"type\":\"uint256\"}],\"name\":\"TokensConsumed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"SUPPORTED_USDC_VERSION\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"applyAllowListUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"remoteTokenAddress\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundRateLimiterConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundRateLimiterConfig\",\"type\":\"tuple\"}],\"internalType\":\"structTokenPool.ChainUpdate[]\",\"name\":\"chains\",\"type\":\"tuple[]\"}],\"name\":\"applyChainUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowList\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowListEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentInboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getCurrentOutboundRateLimiterState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint128\",\"name\":\"tokens\",\"type\":\"uint128\"},{\"internalType\":\"uint32\",\"name\":\"lastUpdated\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.TokenBucket\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"}],\"name\":\"getDomain\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structUSDCTokenPool.Domain\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRateLimitAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemotePool\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRemoteToken\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRmnProxy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSupportedChains\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getToken\",\"outputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"token\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_localDomainIdentifier\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_messageTransmitter\",\"outputs\":[{\"internalType\":\"contractIMessageTransmitter\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_tokenMessenger\",\"outputs\":[{\"internalType\":\"contractITokenMessenger\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"}],\"name\":\"isSupportedChain\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isSupportedToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"}],\"internalType\":\"structPool.LockOrBurnInV1\",\"name\":\"lockOrBurnIn\",\"type\":\"tuple\"}],\"name\":\"lockOrBurn\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destPoolData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.LockOrBurnOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"originalSender\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"localToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"sourcePoolData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"offchainTokenData\",\"type\":\"bytes\"}],\"internalType\":\"structPool.ReleaseOrMintInV1\",\"name\":\"releaseOrMintIn\",\"type\":\"tuple\"}],\"name\":\"releaseOrMint\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"destinationAmount\",\"type\":\"uint256\"}],\"internalType\":\"structPool.ReleaseOrMintOutV1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"outboundConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint128\",\"name\":\"capacity\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"rate\",\"type\":\"uint128\"}],\"internalType\":\"structRateLimiter.Config\",\"name\":\"inboundConfig\",\"type\":\"tuple\"}],\"name\":\"setChainRateLimiterConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"allowedCaller\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"domainIdentifier\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structUSDCTokenPool.DomainUpdate[]\",\"name\":\"domains\",\"type\":\"tuple[]\"}],\"name\":\"setDomains\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rateLimitAdmin\",\"type\":\"address\"}],\"name\":\"setRateLimitAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"remoteChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"remotePoolAddress\",\"type\":\"bytes\"}],\"name\":\"setRemotePool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newRouter\",\"type\":\"address\"}],\"name\":\"setRouter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x6101406040523480156200001257600080fd5b506040516200520538038062005205833981016040819052620000359162000ae9565b83838383336000816200005b57604051639b15e16f60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b03848116919091179091558116156200008e576200008e81620003e9565b50506001600160a01b0384161580620000ae57506001600160a01b038116155b80620000c157506001600160a01b038216155b15620000e0576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c052620001335760408051600081526020810190915262000133908462000463565b5050506001600160a01b038616905062000160576040516306b7c75960e31b815260040160405180910390fd5b6000856001600160a01b0316632c1219216040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001c7919062000c0f565b90506000816001600160a01b03166354fd4d506040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200020a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000230919062000c36565b905063ffffffff81161562000265576040516334697c6b60e11b815263ffffffff821660048201526024015b60405180910390fd5b6000876001600160a01b0316639cdbb1816040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002a6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002cc919062000c36565b905063ffffffff811615620002fd576040516316ba39c560e31b815263ffffffff821660048201526024016200025c565b6001600160a01b0380891660e05283166101008190526040805163234d8e3d60e21b81529051638d3638f4916004808201926020929091908290030181865afa1580156200034f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000375919062000c36565b63ffffffff166101205260e0516080516200039f916001600160a01b0390911690600019620005c0565b6040516001600160a01b03891681527f2e902d38f15b233cbb63711add0fca4545334d3a169d60c0a616494d7eea95449060200160405180910390a1505050505050505062000d83565b336001600160a01b038216036200041357604051636d6c4ee560e11b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c05162000484576040516335f4a7b360e01b815260040160405180910390fd5b60005b82518110156200050f576000838281518110620004a857620004a862000c5e565b60209081029190910101519050620004c2600282620006a6565b1562000505576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b5060010162000487565b5060005b8151811015620005bb57600082828151811062000534576200053462000c5e565b6020026020010151905060006001600160a01b0316816001600160a01b031603620005605750620005b2565b6200056d600282620006c6565b15620005b0576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010162000513565b505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801562000612573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000638919062000c74565b62000644919062000ca4565b604080516001600160a01b038616602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152919250620006a091869190620006dd16565b50505050565b6000620006bd836001600160a01b038416620007ae565b90505b92915050565b6000620006bd836001600160a01b038416620008b2565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200072c906001600160a01b03851690849062000904565b805190915015620005bb57808060200190518101906200074d919062000cba565b620005bb5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016200025c565b60008181526001830160205260408120548015620008a7576000620007d560018362000cde565b8554909150600090620007eb9060019062000cde565b9050808214620008575760008660000182815481106200080f576200080f62000c5e565b906000526020600020015490508087600001848154811062000835576200083562000c5e565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806200086b576200086b62000cf4565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050620006c0565b6000915050620006c0565b6000818152600183016020526040812054620008fb57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620006c0565b506000620006c0565b60606200091584846000856200091d565b949350505050565b606082471015620009805760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016200025c565b600080866001600160a01b031685876040516200099e919062000d30565b60006040518083038185875af1925050503d8060008114620009dd576040519150601f19603f3d011682016040523d82523d6000602084013e620009e2565b606091505b509092509050620009f68783838762000a01565b979650505050505050565b6060831562000a7557825160000362000a6d576001600160a01b0385163b62000a6d5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200025c565b508162000915565b62000915838381511562000a8c5781518083602001fd5b8060405162461bcd60e51b81526004016200025c919062000d4e565b6001600160a01b038116811462000abe57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b805162000ae48162000aa8565b919050565b600080600080600060a0868803121562000b0257600080fd5b855162000b0f8162000aa8565b8095505060208087015162000b248162000aa8565b60408801519095506001600160401b038082111562000b4257600080fd5b818901915089601f83011262000b5757600080fd5b81518181111562000b6c5762000b6c62000ac1565b8060051b604051601f19603f8301168101818110858211171562000b945762000b9462000ac1565b60405291825284820192508381018501918c83111562000bb357600080fd5b938501935b8285101562000bdc5762000bcc8562000ad7565b8452938501939285019262000bb8565b80985050505050505062000bf36060870162000ad7565b915062000c036080870162000ad7565b90509295509295909350565b60006020828403121562000c2257600080fd5b815162000c2f8162000aa8565b9392505050565b60006020828403121562000c4957600080fd5b815163ffffffff8116811462000c2f57600080fd5b634e487b7160e01b600052603260045260246000fd5b60006020828403121562000c8757600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115620006c057620006c062000c8e565b60006020828403121562000ccd57600080fd5b8151801515811462000c2f57600080fd5b81810381811115620006c057620006c062000c8e565b634e487b7160e01b600052603160045260246000fd5b60005b8381101562000d2757818101518382015260200162000d0d565b50506000910152565b6000825162000d4481846020870162000d0a565b9190910192915050565b602081526000825180602084015262000d6f81604085016020870162000d0a565b601f01601f19169190910160400192915050565b60805160a05160c05160e05161010051610120516143c962000e3c60003960008181610382015281816111d401528181611e1d0152611e7b0152600081816106760152610a7801526000818161035b01526110ea01526000818161063a01528181611f18015261282201526000818161057601528181611c1b01526121ce01526000818161028f015281816102e4015281816110b401528181611b3b015281816120ee015281816127b80152612a0d01526143c96000f3fe608060405234801561001057600080fd5b50600436106101ef5760003560e01c80639a4575b91161010f578063c75eea9c116100a2578063dfadfa3511610071578063dfadfa351461059a578063e0351e1314610638578063f2fde38b1461065e578063fbf84dd71461067157600080fd5b8063c75eea9c1461053b578063cf7401f31461054e578063db6327dc14610561578063dc0bd9711461057457600080fd5b8063b0f479a1116100de578063b0f479a1146104e2578063b794658014610500578063c0d7865514610513578063c4bffe2b1461052657600080fd5b80639a4575b9146104365780639fdf13ff14610456578063a7cd63b71461045e578063af58d59f1461047357600080fd5b80636155cda01161018757806379ba50971161015657806379ba5097146103ea5780637d54534e146103f25780638926f54f146104055780638da5cb5b1461041857600080fd5b80636155cda0146103565780636b716b0d1461037d5780636d3d1a58146103b957806378a010b2146103d757600080fd5b806321df0da7116101c357806321df0da71461028d578063240028e8146102d4578063390775371461032157806354c8a4f31461034357600080fd5b806241d3c1146101f457806301ffc9a7146102095780630a2fd49314610231578063181f5a7714610251575b600080fd5b6102076102023660046131b0565b610698565b005b61021c610217366004613225565b610835565b60405190151581526020015b60405180910390f35b61024461023f36600461328d565b61091a565b604051610228919061330e565b6102446040518060400160405280601381526020017f55534443546f6b656e506f6f6c20312e352e300000000000000000000000000081525081565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610228565b61021c6102e236600461334e565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b61033461032f36600461336b565b6109ca565b60405190518152602001610228565b6102076103513660046133f3565b610bb7565b6102af7f000000000000000000000000000000000000000000000000000000000000000081565b6103a47f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610228565b60085473ffffffffffffffffffffffffffffffffffffffff166102af565b6102076103e536600461345f565b610c32565b610207610da1565b61020761040036600461334e565b610e6f565b61021c61041336600461328d565b610ef0565b60015473ffffffffffffffffffffffffffffffffffffffff166102af565b6104496104443660046134e4565b610f07565b604051610228919061351f565b6103a4600081565b61046661124f565b604051610228919061357f565b61048661048136600461328d565b611260565b604051610228919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff166102af565b61024461050e36600461328d565b611335565b61020761052136600461334e565b611360565b61052e611434565b60405161022891906135d9565b61048661054936600461328d565b6114ec565b61020761055c366004613764565b6115be565b61020761056f3660046137ab565b611647565b7f00000000000000000000000000000000000000000000000000000000000000006102af565b61060e6105a836600461328d565b60408051606080820183526000808352602080840182905292840181905267ffffffffffffffff949094168452600982529282902082519384018352805484526001015463ffffffff811691840191909152640100000000900460ff1615159082015290565b604080518251815260208084015163ffffffff169082015291810151151590820152606001610228565b7f000000000000000000000000000000000000000000000000000000000000000061021c565b61020761066c36600461334e565b611acd565b6102af7f000000000000000000000000000000000000000000000000000000000000000081565b6106a0611ae1565b60005b818110156107f75760008383838181106106bf576106bf6137ed565b9050608002018036038101906106d59190613830565b805190915015806106f25750604081015167ffffffffffffffff16155b1561076157604080517fa087bd2900000000000000000000000000000000000000000000000000000000815282516004820152602083015163ffffffff1660248201529082015167ffffffffffffffff1660448201526060820151151560648201526084015b60405180910390fd5b60408051606080820183528351825260208085015163ffffffff9081168285019081529286015115158486019081529585015167ffffffffffffffff166000908152600990925293902091518255516001918201805494511515640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009095169190931617929092179055016106a3565b507f1889010d2535a0ab1643678d1da87fbbe8b87b2f585b47ddb72ec622aef9ee5682826040516108299291906138aa565b60405180910390a15050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf0000000000000000000000000000000000000000000000000000000014806108c857507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b8061091457507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b67ffffffffffffffff8116600090815260076020526040902060040180546060919061094590613931565b80601f016020809104026020016040519081016040528092919081815260200182805461097190613931565b80156109be5780601f10610993576101008083540402835291602001916109be565b820191906000526020600020905b8154815290600101906020018083116109a157829003601f168201915b50505050509050919050565b6040805160208101909152600081526109ea6109e583613a2f565b611b34565b60006109f960c0840184613b24565b810190610a069190613b89565b90506000610a1760e0850185613b24565b810190610a249190613bc8565b9050610a34816000015183611d65565b805160208201516040517f57ecfd2800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016926357ecfd2892610aab92600401613c59565b6020604051808303816000875af1158015610aca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aee9190613c7e565b610b24576040517fbf969f2200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b34606085016040860161334e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f08660600135604051610b9691815260200190565b60405180910390a35050604080516020810190915260609092013582525090565b610bbf611ae1565b610c2c84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250611f1692505050565b50505050565b610c3a611ae1565b610c4383610ef0565b610c85576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610758565b67ffffffffffffffff831660009081526007602052604081206004018054610cac90613931565b80601f0160208091040260200160405190810160405280929190818152602001828054610cd890613931565b8015610d255780601f10610cfa57610100808354040283529160200191610d25565b820191906000526020600020905b815481529060010190602001808311610d0857829003601f168201915b5050505067ffffffffffffffff8616600090815260076020526040902091925050600401610d54838583613ce3565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610d9393929190613e47565b60405180910390a250505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610df2576040517f02b543c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560008054909116815560405173ffffffffffffffffffffffffffffffffffffffff909216929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e77611ae1565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527f44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d091749060200160405180910390a150565b6000610914600567ffffffffffffffff84166120cc565b6040805180820190915260608082526020820152610f2c610f2783613e77565b6120e7565b6000600981610f41604086016020870161328d565b67ffffffffffffffff168152602080820192909252604090810160002081516060810183528154815260019091015463ffffffff81169382019390935264010000000090920460ff161515908201819052909150610fe857610fa9604084016020850161328d565b6040517fd201c48a00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610758565b610ff28380613b24565b9050602014611039576110058380613b24565b6040517fa3c8cf09000000000000000000000000000000000000000000000000000000008152600401610758929190613f1b565b60006110458480613b24565b8101906110529190613f2f565b602083015183516040517ff856ddb60000000000000000000000000000000000000000000000000000000081526060880135600482015263ffffffff90921660248301526044820183905273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116606484015260848301919091529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063f856ddb69060a4016020604051808303816000875af1158015611133573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111579190613f48565b6040516060870135815290915033907f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df79060200160405180910390a260405180604001604052806111b487602001602081019061050e919061328d565b815260408051808201825267ffffffffffffffff851680825263ffffffff7f00000000000000000000000000000000000000000000000000000000000000008116602093840190815284518085019390935251169281019290925290910190606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052905295945050505050565b606061125b60026122b1565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff161515948201949094526003909101548084166060830152919091049091166080820152610914906122be565b67ffffffffffffffff8116600090815260076020526040902060050180546060919061094590613931565b611368611ae1565b73ffffffffffffffffffffffffffffffffffffffff81166113b5576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f16849101610829565b6060600061144260056122b1565b90506000815167ffffffffffffffff8111156114605761146061361b565b604051908082528060200260200182016040528015611489578160200160208202803683370190505b50905060005b82518110156114e5578281815181106114aa576114aa6137ed565b60200260200101518282815181106114c4576114c46137ed565b67ffffffffffffffff9092166020928302919091019091015260010161148f565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff161515948201949094526001909101548084166060830152919091049091166080820152610914906122be565b60085473ffffffffffffffffffffffffffffffffffffffff1633148015906115fe575060015473ffffffffffffffffffffffffffffffffffffffff163314155b15611637576040517f8e4a23d6000000000000000000000000000000000000000000000000000000008152336004820152602401610758565b611642838383612370565b505050565b61164f611ae1565b60005b8181101561164257600083838381811061166e5761166e6137ed565b90506020028101906116809190613f65565b61168990613fa3565b905061169e816080015182602001511561245a565b6116b18160a0015182602001511561245a565b8060200151156119ad5780516116d39060059067ffffffffffffffff16612593565b6117185780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610758565b604081015151158061172d5750606081015151155b15611764576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906119459082614057565b506060820151600582019061195a9082614057565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c295506119a09493929190614171565b60405180910390a1611ac4565b80516119c59060059067ffffffffffffffff1661259f565b611a0a5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610758565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff00000000000000000000000000000000000000000090811682556001820183905560028201805490911690556003810182905590611a736004830182613162565b611a81600583016000613162565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b50600101611652565b611ad5611ae1565b611ade816125ab565b50565b60015473ffffffffffffffffffffffffffffffffffffffff163314611b32576040517f2b5c74de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116911614611bc95760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610758565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611c77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9b9190613c7e565b15611cd2576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611cdf816020015161266f565b6000611cee826020015161091a565b9050805160001480611d12575080805190602001208260a001518051906020012014155b15611d4f578160a001516040517f24eb47e5000000000000000000000000000000000000000000000000000000008152600401610758919061330e565b611d6182602001518360600151612795565b5050565b600482015163ffffffff811615611db0576040517f68d2f8d600000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401610758565b6008830151600c8401516014850151602085015163ffffffff808516911614611e1b5760208501516040517fe366a11700000000000000000000000000000000000000000000000000000000815263ffffffff91821660048201529084166024820152604401610758565b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168263ffffffff1614611eb0576040517f77e4802600000000000000000000000000000000000000000000000000000000815263ffffffff7f00000000000000000000000000000000000000000000000000000000000000008116600483015283166024820152604401610758565b845167ffffffffffffffff828116911614611f0e5784516040517ff917ffea00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff91821660048201529082166024820152604401610758565b505050505050565b7f0000000000000000000000000000000000000000000000000000000000000000611f6d576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8251811015612003576000838281518110611f8d57611f8d6137ed565b60200260200101519050611fab8160026127dc90919063ffffffff16565b15611ffa5760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611f70565b5060005b8151811015611642576000828281518110612024576120246137ed565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361206857506120c4565b6120736002826127fe565b156120c25760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101612007565b600081815260018301602052604081205415155b9392505050565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161461217c5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610758565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa15801561222a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224e9190613c7e565b15612285576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122928160400151612820565b61229f816020015161289f565b611ade816020015182606001516129ed565b606060006120e083612a31565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915261234c82606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff16426123309190614239565b85608001516fffffffffffffffffffffffffffffffff16612a8c565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b61237983610ef0565b6123bb576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84166004820152602401610758565b6123c682600061245a565b67ffffffffffffffff831660009081526007602052604090206123e99083612ab6565b6123f481600061245a565b67ffffffffffffffff8316600090815260076020526040902061241a9060020182612ab6565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b83838360405161244d9392919061424c565b60405180910390a1505050565b8151156125215781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff161015806124b0575060408201516fffffffffffffffffffffffffffffffff16155b156124e957816040517f8020d12400000000000000000000000000000000000000000000000000000000815260040161075891906142cf565b8015611d61576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff1615158061255a575060208201516fffffffffffffffffffffffffffffffff1615155b15611d6157816040517fd68af9cc00000000000000000000000000000000000000000000000000000000815260040161075891906142cf565b60006120e08383612c58565b60006120e08383612ca7565b3373ffffffffffffffffffffffffffffffffffffffff8216036125fa576040517fdad89dca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217835560015460405192939116917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61267881610ef0565b6126ba576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610758565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015612739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275d9190613c7e565b611ade576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610758565b67ffffffffffffffff82166000908152600760205260409020611d6190600201827f0000000000000000000000000000000000000000000000000000000000000000612d9a565b60006120e08373ffffffffffffffffffffffffffffffffffffffff8416612ca7565b60006120e08373ffffffffffffffffffffffffffffffffffffffff8416612c58565b7f000000000000000000000000000000000000000000000000000000000000000015611ade5761285160028261311d565b611ade576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610758565b6128a881610ef0565b6128ea576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff82166004820152602401610758565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612963573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612987919061430b565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ade576040517f728fe07b000000000000000000000000000000000000000000000000000000008152336004820152602401610758565b67ffffffffffffffff82166000908152600760205260409020611d6190827f0000000000000000000000000000000000000000000000000000000000000000612d9a565b6060816000018054806020026020016040519081016040528092919081815260200182805480156109be57602002820191906000526020600020905b815481526020019060010190808311612a6d5750505050509050919050565b6000612aab85612a9c8486614328565b612aa6908761433f565b61314c565b90505b949350505050565b8154600090612adf90700100000000000000000000000000000000900463ffffffff1642614239565b90508015612b815760018301548354612b27916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612a8c565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612ba7916fffffffffffffffffffffffffffffffff908116911661314c565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c199061244d9084906142cf565b6000818152600183016020526040812054612c9f57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610914565b506000610914565b60008181526001830160205260408120548015612d90576000612ccb600183614239565b8554909150600090612cdf90600190614239565b9050808214612d44576000866000018281548110612cff57612cff6137ed565b9060005260206000200154905080876000018481548110612d2257612d226137ed565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612d5557612d55614352565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610914565b6000915050610914565b825474010000000000000000000000000000000000000000900460ff161580612dc1575081155b15612dcb57505050565b825460018401546fffffffffffffffffffffffffffffffff80831692911690600090612e1190700100000000000000000000000000000000900463ffffffff1642614239565b90508015612ed15781831115612e53576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001860154612e8d9083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612a8c565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b84821015612f885773ffffffffffffffffffffffffffffffffffffffff8416612f30576040517ff94ebcd10000000000000000000000000000000000000000000000000000000081526004810183905260248101869052604401610758565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff85166044820152606401610758565b8483101561309b5760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16906000908290612fcc9082614239565b612fd6878a614239565b612fe0919061433f565b612fea9190614381565b905073ffffffffffffffffffffffffffffffffffffffff8616613043576040517f15279c080000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610758565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff87166044820152606401610758565b6130a58584614239565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415156120e0565b600081831061315b57816120e0565b5090919050565b50805461316e90613931565b6000825580601f1061317e575050565b601f016020900490600052602060002090810190611ade91905b808211156131ac5760008155600101613198565b5090565b600080602083850312156131c357600080fd5b823567ffffffffffffffff808211156131db57600080fd5b818501915085601f8301126131ef57600080fd5b8135818111156131fe57600080fd5b8660208260071b850101111561321357600080fd5b60209290920196919550909350505050565b60006020828403121561323757600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146120e057600080fd5b67ffffffffffffffff81168114611ade57600080fd5b803561328881613267565b919050565b60006020828403121561329f57600080fd5b81356120e081613267565b6000815180845260005b818110156132d0576020818501810151868301820152016132b4565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006120e060208301846132aa565b73ffffffffffffffffffffffffffffffffffffffff81168114611ade57600080fd5b803561328881613321565b60006020828403121561336057600080fd5b81356120e081613321565b60006020828403121561337d57600080fd5b813567ffffffffffffffff81111561339457600080fd5b820161010081850312156120e057600080fd5b60008083601f8401126133b957600080fd5b50813567ffffffffffffffff8111156133d157600080fd5b6020830191508360208260051b85010111156133ec57600080fd5b9250929050565b6000806000806040858703121561340957600080fd5b843567ffffffffffffffff8082111561342157600080fd5b61342d888389016133a7565b9096509450602087013591508082111561344657600080fd5b50613453878288016133a7565b95989497509550505050565b60008060006040848603121561347457600080fd5b833561347f81613267565b9250602084013567ffffffffffffffff8082111561349c57600080fd5b818601915086601f8301126134b057600080fd5b8135818111156134bf57600080fd5b8760208285010111156134d157600080fd5b6020830194508093505050509250925092565b6000602082840312156134f657600080fd5b813567ffffffffffffffff81111561350d57600080fd5b820160a081850312156120e057600080fd5b60208152600082516040602084015261353b60608401826132aa565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084830301604085015261357682826132aa565b95945050505050565b6020808252825182820181905260009190848201906040850190845b818110156135cd57835173ffffffffffffffffffffffffffffffffffffffff168352928401929184019160010161359b565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156135cd57835167ffffffffffffffff16835292840192918401916001016135f5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff8111828210171561366e5761366e61361b565b60405290565b6040805190810167ffffffffffffffff8111828210171561366e5761366e61361b565b60405160c0810167ffffffffffffffff8111828210171561366e5761366e61361b565b8015158114611ade57600080fd5b8035613288816136ba565b80356fffffffffffffffffffffffffffffffff8116811461328857600080fd5b60006060828403121561370557600080fd5b6040516060810181811067ffffffffffffffff821117156137285761372861361b565b6040529050808235613739816136ba565b8152613747602084016136d3565b6020820152613758604084016136d3565b60408201525092915050565b600080600060e0848603121561377957600080fd5b833561378481613267565b925061379385602086016136f3565b91506137a285608086016136f3565b90509250925092565b600080602083850312156137be57600080fd5b823567ffffffffffffffff8111156137d557600080fd5b6137e1858286016133a7565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b803563ffffffff8116811461328857600080fd5b60006080828403121561384257600080fd5b6040516080810181811067ffffffffffffffff821117156138655761386561361b565b604052823581526138786020840161381c565b6020820152604083013561388b81613267565b6040820152606083013561389e816136ba565b60608201529392505050565b6020808252818101839052600090604080840186845b87811015613924578135835263ffffffff6138dc86840161381c565b1685840152838201356138ee81613267565b67ffffffffffffffff168385015260608281013561390b816136ba565b15159084015260809283019291909101906001016138c0565b5090979650505050505050565b600181811c9082168061394557607f821691505b60208210810361397e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082601f83011261399557600080fd5b813567ffffffffffffffff808211156139b0576139b061361b565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156139f6576139f661361b565b81604052838152866020858801011115613a0f57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006101008236031215613a4257600080fd5b613a4a61364a565b823567ffffffffffffffff80821115613a6257600080fd5b613a6e36838701613984565b8352613a7c6020860161327d565b6020840152613a8d60408601613343565b604084015260608501356060840152613aa860808601613343565b608084015260a0850135915080821115613ac157600080fd5b613acd36838701613984565b60a084015260c0850135915080821115613ae657600080fd5b613af236838701613984565b60c084015260e0850135915080821115613b0b57600080fd5b50613b1836828601613984565b60e08301525092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613b5957600080fd5b83018035915067ffffffffffffffff821115613b7457600080fd5b6020019150368190038213156133ec57600080fd5b600060408284031215613b9b57600080fd5b613ba3613674565b8235613bae81613267565b8152613bbc6020840161381c565b60208201529392505050565b600060208284031215613bda57600080fd5b813567ffffffffffffffff80821115613bf257600080fd5b9083019060408286031215613c0657600080fd5b613c0e613674565b823582811115613c1d57600080fd5b613c2987828601613984565b825250602083013582811115613c3e57600080fd5b613c4a87828601613984565b60208301525095945050505050565b604081526000613c6c60408301856132aa565b828103602084015261357681856132aa565b600060208284031215613c9057600080fd5b81516120e0816136ba565b601f821115611642576000816000526020600020601f850160051c81016020861015613cc45750805b601f850160051c820191505b81811015611f0e57828155600101613cd0565b67ffffffffffffffff831115613cfb57613cfb61361b565b613d0f83613d098354613931565b83613c9b565b6000601f841160018114613d615760008515613d2b5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355613df7565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015613db05786850135825560209485019460019092019101613d90565b5086821015613deb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b604081526000613e5a60408301866132aa565b8281036020840152613e6d818587613dfe565b9695505050505050565b600060a08236031215613e8957600080fd5b60405160a0810167ffffffffffffffff8282108183111715613ead57613ead61361b565b816040528435915080821115613ec257600080fd5b50613ecf36828601613984565b8252506020830135613ee081613267565b60208201526040830135613ef381613321565b6040820152606083810135908201526080830135613f1081613321565b608082015292915050565b602081526000612aae602083018486613dfe565b600060208284031215613f4157600080fd5b5035919050565b600060208284031215613f5a57600080fd5b81516120e081613267565b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec1833603018112613f9957600080fd5b9190910192915050565b60006101408236031215613fb657600080fd5b613fbe613697565b613fc78361327d565b8152613fd5602084016136c8565b6020820152604083013567ffffffffffffffff80821115613ff557600080fd5b61400136838701613984565b6040840152606085013591508082111561401a57600080fd5b5061402736828601613984565b60608301525061403a36608085016136f3565b608082015261404c3660e085016136f3565b60a082015292915050565b815167ffffffffffffffff8111156140715761407161361b565b6140858161407f8454613931565b84613c9b565b602080601f8311600181146140d857600084156140a25750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555611f0e565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561412557888601518255948401946001909101908401614106565b508582101561416157878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff87168352806020840152614195818401876132aa565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506141d39050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152613576565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156109145761091461420a565b67ffffffffffffffff8416815260e0810161429860208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612aae565b6060810161091482848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b60006020828403121561431d57600080fd5b81516120e081613321565b80820281158282048414176109145761091461420a565b808201808211156109145761091461420a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000826143b7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea164736f6c6343000818000a", } var USDCTokenPoolABI = USDCTokenPoolMetaData.ABI @@ -2435,6 +2435,123 @@ func (_USDCTokenPool *USDCTokenPoolFilterer) ParseOwnershipTransferred(log types return event, nil } +type USDCTokenPoolRateLimitAdminSetIterator struct { + Event *USDCTokenPoolRateLimitAdminSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *USDCTokenPoolRateLimitAdminSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(USDCTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(USDCTokenPoolRateLimitAdminSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *USDCTokenPoolRateLimitAdminSetIterator) Error() error { + return it.fail +} + +func (it *USDCTokenPoolRateLimitAdminSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type USDCTokenPoolRateLimitAdminSet struct { + RateLimitAdmin common.Address + Raw types.Log +} + +func (_USDCTokenPool *USDCTokenPoolFilterer) FilterRateLimitAdminSet(opts *bind.FilterOpts) (*USDCTokenPoolRateLimitAdminSetIterator, error) { + + logs, sub, err := _USDCTokenPool.contract.FilterLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return &USDCTokenPoolRateLimitAdminSetIterator{contract: _USDCTokenPool.contract, event: "RateLimitAdminSet", logs: logs, sub: sub}, nil +} + +func (_USDCTokenPool *USDCTokenPoolFilterer) WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *USDCTokenPoolRateLimitAdminSet) (event.Subscription, error) { + + logs, sub, err := _USDCTokenPool.contract.WatchLogs(opts, "RateLimitAdminSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(USDCTokenPoolRateLimitAdminSet) + if err := _USDCTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_USDCTokenPool *USDCTokenPoolFilterer) ParseRateLimitAdminSet(log types.Log) (*USDCTokenPoolRateLimitAdminSet, error) { + event := new(USDCTokenPoolRateLimitAdminSet) + if err := _USDCTokenPool.contract.UnpackLog(event, "RateLimitAdminSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type USDCTokenPoolReleasedIterator struct { Event *USDCTokenPoolReleased @@ -2964,6 +3081,8 @@ func (_USDCTokenPool *USDCTokenPool) ParseLog(log types.Log) (generated.AbigenLo return _USDCTokenPool.ParseOwnershipTransferRequested(log) case _USDCTokenPool.abi.Events["OwnershipTransferred"].ID: return _USDCTokenPool.ParseOwnershipTransferred(log) + case _USDCTokenPool.abi.Events["RateLimitAdminSet"].ID: + return _USDCTokenPool.ParseRateLimitAdminSet(log) case _USDCTokenPool.abi.Events["Released"].ID: return _USDCTokenPool.ParseReleased(log) case _USDCTokenPool.abi.Events["RemotePoolSet"].ID: @@ -3030,6 +3149,10 @@ func (USDCTokenPoolOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (USDCTokenPoolRateLimitAdminSet) Topic() common.Hash { + return common.HexToHash("0x44676b5284b809a22248eba0da87391d79098be38bb03154be88a58bf4d09174") +} + func (USDCTokenPoolReleased) Topic() common.Hash { return common.HexToHash("0x2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f52") } @@ -3193,6 +3316,12 @@ type USDCTokenPoolInterface interface { ParseOwnershipTransferred(log types.Log) (*USDCTokenPoolOwnershipTransferred, error) + FilterRateLimitAdminSet(opts *bind.FilterOpts) (*USDCTokenPoolRateLimitAdminSetIterator, error) + + WatchRateLimitAdminSet(opts *bind.WatchOpts, sink chan<- *USDCTokenPoolRateLimitAdminSet) (event.Subscription, error) + + ParseRateLimitAdminSet(log types.Log) (*USDCTokenPoolRateLimitAdminSet, error) + FilterReleased(opts *bind.FilterOpts, sender []common.Address, recipient []common.Address) (*USDCTokenPoolReleasedIterator, error) WatchReleased(opts *bind.WatchOpts, sink chan<- *USDCTokenPoolReleased, sender []common.Address, recipient []common.Address) (event.Subscription, 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 850bfe7c410..4b1807d4e1d 100644 --- a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,13 +1,13 @@ GETH_VERSION: 1.13.8 -burn_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.bin 62c7636f6f5b56d1fdc3b8a190a07648ffb6fc5e8351f20fa8902bc107564a6b -burn_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.bin 7ab444f3e3df021338fc1ae33e1cc48d59537f78ee4c3e9ff23de10903736c4b -burn_with_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.bin cea4f4afb612900dbd893c4457a6bf47c562544e1219dd97bee0249680e36f10 +burn_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.bin d5a1028728ed52d3c12ccd0e2f54d536697a6d5f689b0e89a4d083011a8cb1f6 +burn_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.bin 7f6b367ccf37878317fd9f50488370770204f0cc10c6e0e576be7e7c4ca8db56 +burn_with_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.bin e136c9f7a1d7af46ed5bd5bb836317c97715a71ee024868251abd0c462f1f115 ccip_encoding_utils: ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.abi ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.bin 9971fc93c34442a0989570d3dab90a125de31e6e60754ad972807ce6ad4dfba0 ccip_home: ../../../contracts/solc/v0.8.24/CCIPHome/CCIPHome.abi ../../../contracts/solc/v0.8.24/CCIPHome/CCIPHome.bin 02cb75b4274a5be7f4006cf2b72cc09e77eb6dba4c1a9c720af86668ff8ea1df ccip_reader_tester: ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.abi ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.bin 893c9930e874fe5235db24e28a22650c37f562da94fac93618566bcd84839fdc ether_sender_receiver: ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.abi ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.bin 09510a3f773f108a3c231e8d202835c845ded862d071ec54c4f89c12d868b8de fee_quoter: ../../../contracts/solc/v0.8.24/FeeQuoter/FeeQuoter.abi ../../../contracts/solc/v0.8.24/FeeQuoter/FeeQuoter.bin 8a0869d14bb5247fbc6d836fc20d123358373ed688e0d3b387d59e7d05496fea -lock_release_token_pool: ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.bin 4c7de56b70c1588b867de95d5b4b380020149de4f61e2155a304cf590b921783 +lock_release_token_pool: ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/LockReleaseTokenPool.bin b30d5520449d57a4fffa3c3675e46d50ad29b066e09c16971153538a38ab25f7 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 ec2d3a92348d8e7b8f0d359b62a45157b9d2c750c01fbcf991826c4392f6e218 mock_usdc_token_messenger: ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.bin d976651d36b33ac2196b32b9d2f4fa6690c6a18d41b621365659fce1c1d1e737 @@ -19,14 +19,14 @@ nonce_manager: ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.abi ../ offramp: ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.abi ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.bin d20e6c0baf08926b341c31ed0018983e135a75b7d120591de49ca4ece3824d0b onramp: ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.abi ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.bin 2bf74188a997218502031f177cb2df505b272d66b25fd341a741289e77380c59 ping_pong_demo: ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin 24b4415a883a470d65c484be0fa20714a46b1c9262db205f1c958017820307b2 -registry_module_owner_custom: ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.abi ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.bin 75be86323c227917a9bbc3f799d7ed02f92db546653a36db30ed0ebe64461353 +registry_module_owner_custom: ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.abi ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.bin 0fc277a0b512db4e20b5a32a775b94ed2c0d342d8237511de78c94f7dacad428 report_codec: ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.abi ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.bin 6c943b39f003aa67c3cefa19a8ff99e846236a058e1ceae77569c3a065ffd5c7 rmn_home: ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.abi ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.bin 84ca84b3d0c00949905a3d10a91255f877cf32b2a0d7f7f7ce3121ced34a8cb7 rmn_proxy_contract: ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.abi ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.bin b048d8e752e3c41113ebb305c1efa06737ad36b4907b93e627fb0a3113023454 rmn_remote: ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.abi ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.bin faee0b0cdbe67f2e28deccf12acd4df13dd90992f6cbc0ba17bab845b8f4eb1c router: ../../../contracts/solc/v0.8.24/Router/Router.abi ../../../contracts/solc/v0.8.24/Router/Router.bin 2e4f0a7826c8abb49d882bb49fc5ff20a186dbd3137624b9097ffed903ae4888 token_admin_registry: ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.abi ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.bin 397bc7be08c2848c0f4715f90b16206d6367f78ffb7cd48e2b1dfc0ccc5aea26 -token_pool: ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.abi ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.bin fe0a6ea406e8d6c9330b6efabff0514cbb3e1aa4c51853507c2aef82f4a3f2ad +token_pool: ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.abi ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.bin 7956641010fdb65dc2cf5cc1a51c5ed3e0c32493d6916eb563d24a855e827342 usdc_reader_tester: ../../../contracts/solc/v0.8.24/USDCReaderTester/USDCReaderTester.abi ../../../contracts/solc/v0.8.24/USDCReaderTester/USDCReaderTester.bin 672a07c9218fd6ad7c04dde583088b0f5ffc8d55a46f4be1714008dd3409438b -usdc_token_pool: ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.abi ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.bin 9b976c2982909d0019959ab93340e016c5c7e445d985a181c9e55c7c567cc544 +usdc_token_pool: ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.abi ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.bin da68b8ea71a12762d9fd3581cabddcb1c6f5b64a3fe3923842216dbf9d2aa9c6 weth9: ../../../contracts/solc/v0.8.24/WETH9/WETH9.abi ../../../contracts/solc/v0.8.24/WETH9/WETH9.bin 2970d79a0ca6dd6279cde130de45e56c8790ed695eae477fb5ba4c1bb75b720d diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c347f0fb3f7..dae73cfbfc0 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -289,13 +289,13 @@ require ( github.com/shirou/gopsutil/v3 v3.24.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 // indirect github.com/smartcontractkit/chain-selectors v1.0.27 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect - github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 // indirect + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 3e5bc90f90f..0d158c973e0 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1090,8 +1090,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= @@ -1104,8 +1104,8 @@ github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 h1:1xTm8UGeD github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 h1:ZUihu/AMiFkZgO5XkVcpFayhIUibdovHzpbHnMPZUr0= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 h1:YsE0uS6S10oAWnFbjNDc7tN9JrWYjvyqMnTSbTSgl00= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 h1:B4DFdk6MGcQnoCjjMBCx7Z+GWQpxRWJ4O8W/dVJyWGA= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8/go.mod h1:WkBqgBo+g34Gm5vWkDDl8Fh3Mzd7bF5hXp7rryg0t5o= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 112b87cf0af..2c918b3a8d8 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -376,7 +376,9 @@ func NewApplication(opts ApplicationOpts) (Application, error) { if err != nil { return nil, errors.Wrap(err, "NewApplication: failed to initialize LDAP Authentication module") } - sessionReaper = ldapauth.NewLDAPServerStateSync(opts.DS, cfg.WebServer().LDAP(), globalLogger) + syncer := ldapauth.NewLDAPServerStateSyncer(opts.DS, cfg.WebServer().LDAP(), globalLogger) + srvcs = append(srvcs, syncer) + sessionReaper = utils.NewSleeperTaskCtx(syncer) case sessions.LocalAuth: authenticationProvider = localauth.NewORM(opts.DS, cfg.WebServer().SessionTimeout().Duration(), globalLogger, auditLogger) sessionReaper = localauth.NewSessionReaper(opts.DS, cfg.WebServer(), globalLogger) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index f5a3aaa7b59..c6dd58369d0 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -752,6 +752,7 @@ func TestConfig_Marshal(t *testing.T) { ComputeUnitPriceDefault: ptr[uint64](100), FeeBumpPeriod: commoncfg.MustNewDuration(time.Minute), BlockHistoryPollPeriod: commoncfg.MustNewDuration(time.Minute), + BlockHistorySize: ptr[uint64](1), ComputeUnitLimitDefault: ptr[uint32](100_000), EstimateComputeUnitLimit: ptr(false), }, @@ -1278,6 +1279,7 @@ ComputeUnitPriceMin = 10 ComputeUnitPriceDefault = 100 FeeBumpPeriod = '1m0s' BlockHistoryPollPeriod = '1m0s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 100000 EstimateComputeUnitLimit = false diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 44362761bd1..f6cb497f7c8 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -499,6 +499,7 @@ ComputeUnitPriceMin = 10 ComputeUnitPriceDefault = 100 FeeBumpPeriod = '1m0s' BlockHistoryPollPeriod = '1m0s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 100000 EstimateComputeUnitLimit = false diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 86612f05798..4d25d23c333 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -660,6 +660,7 @@ ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' BlockHistoryPollPeriod = '5s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 200000 EstimateComputeUnitLimit = false @@ -703,6 +704,7 @@ ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' BlockHistoryPollPeriod = '5s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 200000 EstimateComputeUnitLimit = false diff --git a/core/services/keystore/eth.go b/core/services/keystore/eth.go index ee1c4f23a91..f69bbec28d2 100644 --- a/core/services/keystore/eth.go +++ b/core/services/keystore/eth.go @@ -8,9 +8,11 @@ import ( "strings" "sync" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" @@ -35,6 +37,7 @@ type Eth interface { SubscribeToKeyChanges(ctx context.Context) (ch chan struct{}, unsub func()) SignTx(ctx context.Context, fromAddress common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) + SignMessage(ctx context.Context, address common.Address, message []byte) ([]byte, error) EnabledKeysForChain(ctx context.Context, chainID *big.Int) (keys []ethkey.KeyV2, err error) GetRoundRobinAddress(ctx context.Context, chainID *big.Int, addresses ...common.Address) (address common.Address, err error) @@ -532,6 +535,25 @@ func (ks *eth) XXXTestingOnlyAdd(ctx context.Context, key ethkey.KeyV2) { } } +// SignMessage signs the provided message using the private key associated with the given address, +// following the EIP-191 specific identifier (e.g., keccak256("\x19Ethereum Signed Message:\n"${message length}${message})) +func (ks *eth) SignMessage(ctx context.Context, address common.Address, data []byte) ([]byte, error) { + ks.lock.RLock() + defer ks.lock.RUnlock() + if ks.isLocked() { + return nil, ErrLocked + } + key, err := ks.getByID(address.Hex()) + if err != nil { + return nil, err + } + signature, err := crypto.Sign(accounts.TextHash(data), key.ToEcdsaPrivKey()) + if err != nil { + return nil, errors.Wrap(err, "failed to sign data") + } + return signature, nil +} + // caller must hold lock! func (ks *eth) getByID(id string) (ethkey.KeyV2, error) { key, found := ks.keyRing.Eth[id] diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index dce11865b28..1b3ac10afc4 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -9,7 +9,9 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -357,6 +359,31 @@ func Test_EthKeyStore_SignTx(t *testing.T) { require.NotEqual(t, tx, signed) } +func Test_EthKeyStore_SignMessage(t *testing.T) { + t.Parallel() + + ctx := testutils.Context(t) + + db := pgtest.NewSqlxDB(t) + keyStore := cltest.NewKeyStore(t, db) + ethKeyStore := keyStore.Eth() + + k, _ := cltest.MustInsertRandomKey(t, ethKeyStore) + + pubKeyBytes := crypto.FromECDSAPub(&k.ToEcdsaPrivKey().PublicKey) + + message := []byte("this is a message") + + signedMessage, err := keyStore.Eth().SignMessage(ctx, k.Address, message) + require.NoError(t, err) + sigPublicKey, err := crypto.Ecrecover(accounts.TextHash(message), signedMessage) + require.NoError(t, err) + require.Equal(t, pubKeyBytes, sigPublicKey) + + _, err = keyStore.Eth().SignMessage(ctx, utils.RandomAddress(), message) + require.ErrorContains(t, err, "Key not found") +} + func Test_EthKeyStore_E2E(t *testing.T) { t.Parallel() diff --git a/core/services/keystore/keys/workflowkey/export.go b/core/services/keystore/keys/workflowkey/export.go new file mode 100644 index 00000000000..bb0430a6701 --- /dev/null +++ b/core/services/keystore/keys/workflowkey/export.go @@ -0,0 +1,44 @@ +package workflowkey + +import ( + "github.com/ethereum/go-ethereum/accounts/keystore" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +const keyTypeIdentifier = "Workflow" + +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 + }, + ) +} + +func (k Key) ToEncryptedJSON(password string, scryptParams utils.ScryptParams) (export []byte, err error) { + return keys.ToEncryptedJSON( + keyTypeIdentifier, + k.Raw(), + k, + password, + scryptParams, + adulteratedPassword, + func(id string, key Key, cryptoJSON keystore.CryptoJSON) keys.EncryptedKeyExport { + return keys.EncryptedKeyExport{ + KeyType: id, + PublicKey: key.PublicKeyString(), + Crypto: cryptoJSON, + } + }, + ) +} + +func adulteratedPassword(password string) string { + return "workflowkey" + password +} diff --git a/core/services/keystore/keys/workflowkey/key.go b/core/services/keystore/keys/workflowkey/key.go new file mode 100644 index 00000000000..ce8560303e0 --- /dev/null +++ b/core/services/keystore/keys/workflowkey/key.go @@ -0,0 +1,107 @@ +package workflowkey + +import ( + cryptorand "crypto/rand" + "encoding/hex" + "errors" + "fmt" + + "golang.org/x/crypto/curve25519" + "golang.org/x/crypto/nacl/box" +) + +type Raw []byte + +func (raw Raw) Key() Key { + privateKey := [32]byte(raw) + return Key{ + privateKey: &privateKey, + publicKey: curve25519PubKeyFromPrivateKey(privateKey), + } +} + +func (raw Raw) String() string { + return fmt.Sprintf("<%s Raw Private Key>", keyTypeIdentifier) +} + +func (raw Raw) GoString() string { + return raw.String() +} + +func (raw Raw) Bytes() []byte { + return ([]byte)(raw) +} + +type Key struct { + privateKey *[curve25519.PointSize]byte + publicKey *[curve25519.PointSize]byte +} + +func New() (Key, error) { + publicKey, privateKey, err := box.GenerateKey(cryptorand.Reader) + if err != nil { + return Key{}, err + } + + return Key{ + privateKey: privateKey, + publicKey: publicKey, + }, nil +} + +func (k Key) PublicKey() [curve25519.PointSize]byte { + return *k.publicKey +} + +func (k Key) PublicKeyString() string { + return hex.EncodeToString(k.publicKey[:]) +} + +func (k Key) ID() string { + return k.PublicKeyString() +} + +func (k Key) Raw() Raw { + raw := make([]byte, curve25519.PointSize) + copy(raw, k.privateKey[:]) + return Raw(raw) +} + +func (k Key) String() string { + return fmt.Sprintf("%sKey{PrivateKey: <redacted>, PublicKey: %s}", keyTypeIdentifier, *k.publicKey) +} + +func (k Key) GoString() string { + return k.String() +} + +// Encrypt encrypts a message using the public key +func (k Key) Encrypt(plaintext []byte) ([]byte, error) { + publicKey := k.PublicKey() + encrypted, err := box.SealAnonymous(nil, plaintext, &publicKey, cryptorand.Reader) + if err != nil { + return nil, err + } + + return encrypted, nil +} + +// Decrypt decrypts a message that was encrypted using the private key +func (k Key) Decrypt(ciphertext []byte) (plaintext []byte, err error) { + publicKey := k.PublicKey() + decrypted, success := box.OpenAnonymous(nil, ciphertext, &publicKey, k.privateKey) + if !success { + return nil, errors.New("decryption failed") + } + + return decrypted, nil +} + +func curve25519PubKeyFromPrivateKey(privateKey [curve25519.PointSize]byte) *[curve25519.PointSize]byte { + var publicKey [curve25519.PointSize]byte + + // Derive the public key + curve25519.ScalarBaseMult(&publicKey, &privateKey) + + return &publicKey +} diff --git a/core/services/keystore/keys/workflowkey/key_test.go b/core/services/keystore/keys/workflowkey/key_test.go new file mode 100644 index 00000000000..3e3a9413a90 --- /dev/null +++ b/core/services/keystore/keys/workflowkey/key_test.go @@ -0,0 +1,88 @@ +package workflowkey + +import ( + cryptorand "crypto/rand" + "encoding/hex" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/nacl/box" +) + +func TestNew(t *testing.T) { + key, err := New() + require.NoError(t, err) + + assert.NotNil(t, key.PublicKey) + assert.NotNil(t, key.privateKey) +} + +func TestPublicKey(t *testing.T) { + key, err := New() + require.NoError(t, err) + + assert.Equal(t, *key.publicKey, key.PublicKey()) +} + +func TestEncryptKeyRawPrivateKey(t *testing.T) { + privKey, err := New() + require.NoError(t, err) + + privateKey := privKey.Raw() + + assert.Equal(t, "<Workflow Raw Private Key>", privateKey.String()) + assert.Equal(t, privateKey.String(), privateKey.GoString()) +} + +func TestEncryptKeyFromRawPrivateKey(t *testing.T) { + boxPubKey, boxPrivKey, err := box.GenerateKey(cryptorand.Reader) + require.NoError(t, err) + + privKey := make([]byte, 32) + copy(privKey, boxPrivKey[:]) + key := Raw(privKey).Key() + + assert.Equal(t, boxPubKey, key.publicKey) + assert.Equal(t, boxPrivKey, key.privateKey) + assert.Equal(t, key.String(), key.GoString()) + + byteBoxPubKey := make([]byte, 32) + copy(byteBoxPubKey, boxPubKey[:]) + + assert.Equal(t, hex.EncodeToString(byteBoxPubKey), key.PublicKeyString()) + assert.Equal(t, fmt.Sprintf("WorkflowKey{PrivateKey: <redacted>, PublicKey: %s}", byteBoxPubKey), key.String()) +} + +func TestPublicKeyStringAndID(t *testing.T) { + key := "my-test-public-key" + var pubkey [32]byte + copy(pubkey[:], key) + k := Key{ + publicKey: &pubkey, + } + + expected := hex.EncodeToString([]byte(key)) + // given the key is a [32]byte we need to ensure the encoded string is 64 character long + for len(expected) < 64 { + expected += "0" + } + + assert.Equal(t, expected, k.PublicKeyString()) + assert.Equal(t, expected, k.ID()) +} + +func TestDecrypt(t *testing.T) { + key, err := New() + require.NoError(t, err) + + secret := []byte("my-secret") + ciphertext, err := key.Encrypt(secret) + require.NoError(t, err) + + plaintext, err := key.Decrypt(ciphertext) + require.NoError(t, err) + + assert.Equal(t, secret, plaintext) +} diff --git a/core/services/keystore/master.go b/core/services/keystore/master.go index 47136f1f2ec..72677a166a3 100644 --- a/core/services/keystore/master.go +++ b/core/services/keystore/master.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/workflowkey" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -45,6 +46,7 @@ type Master interface { StarkNet() StarkNet Aptos() Aptos VRF() VRF + Workflow() Workflow Unlock(ctx context.Context, password string) error IsEmpty(ctx context.Context) (bool, error) } @@ -61,6 +63,7 @@ type master struct { starknet *starknet aptos *aptos vrf *vrf + workflow *workflow } func New(ds sqlutil.DataSource, scryptParams utils.ScryptParams, lggr logger.Logger) Master { @@ -89,6 +92,7 @@ func newMaster(ds sqlutil.DataSource, scryptParams utils.ScryptParams, lggr logg starknet: newStarkNetKeyStore(km), aptos: newAptosKeyStore(km), vrf: newVRFKeyStore(km), + workflow: newWorkflowKeyStore(km), } } @@ -132,6 +136,10 @@ func (ks *master) VRF() VRF { return ks.vrf } +func (ks *master) Workflow() Workflow { + return ks.workflow +} + type ORM interface { isEmpty(context.Context) (bool, error) saveEncryptedKeyRing(context.Context, *encryptedKeyRing, ...func(sqlutil.DataSource) error) error @@ -267,6 +275,8 @@ func GetFieldNameForKey(unknownKey Key) (string, error) { return "Aptos", nil case vrfkey.KeyV2: return "VRF", nil + case workflowkey.Key: + return "Workflow", nil } return "", fmt.Errorf("unknown key type: %T", unknownKey) } diff --git a/core/services/keystore/mocks/eth.go b/core/services/keystore/mocks/eth.go index e28a6d61342..4f2486464eb 100644 --- a/core/services/keystore/mocks/eth.go +++ b/core/services/keystore/mocks/eth.go @@ -1082,6 +1082,66 @@ func (_c *Eth_Import_Call) RunAndReturn(run func(context.Context, []byte, string return _c } +// SignMessage provides a mock function with given fields: ctx, address, message +func (_m *Eth) SignMessage(ctx context.Context, address common.Address, message []byte) ([]byte, error) { + ret := _m.Called(ctx, address, message) + + if len(ret) == 0 { + panic("no return value specified for SignMessage") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, []byte) ([]byte, error)); ok { + return rf(ctx, address, message) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, []byte) []byte); ok { + r0 = rf(ctx, address, message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, []byte) error); ok { + r1 = rf(ctx, address, message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Eth_SignMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SignMessage' +type Eth_SignMessage_Call struct { + *mock.Call +} + +// SignMessage is a helper method to define mock.On call +// - ctx context.Context +// - address common.Address +// - message []byte +func (_e *Eth_Expecter) SignMessage(ctx interface{}, address interface{}, message interface{}) *Eth_SignMessage_Call { + return &Eth_SignMessage_Call{Call: _e.mock.On("SignMessage", ctx, address, message)} +} + +func (_c *Eth_SignMessage_Call) Run(run func(ctx context.Context, address common.Address, message []byte)) *Eth_SignMessage_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].([]byte)) + }) + return _c +} + +func (_c *Eth_SignMessage_Call) Return(_a0 []byte, _a1 error) *Eth_SignMessage_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Eth_SignMessage_Call) RunAndReturn(run func(context.Context, common.Address, []byte) ([]byte, error)) *Eth_SignMessage_Call { + _c.Call.Return(run) + return _c +} + // SignTx provides a mock function with given fields: ctx, fromAddress, tx, chainID func (_m *Eth) SignTx(ctx context.Context, fromAddress common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { ret := _m.Called(ctx, fromAddress, tx, chainID) diff --git a/core/services/keystore/mocks/master.go b/core/services/keystore/mocks/master.go index 687449db98d..7c86001bc54 100644 --- a/core/services/keystore/mocks/master.go +++ b/core/services/keystore/mocks/master.go @@ -595,6 +595,53 @@ func (_c *Master_VRF_Call) RunAndReturn(run func() keystore.VRF) *Master_VRF_Cal return _c } +// Workflow provides a mock function with given fields: +func (_m *Master) Workflow() keystore.Workflow { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Workflow") + } + + var r0 keystore.Workflow + if rf, ok := ret.Get(0).(func() keystore.Workflow); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(keystore.Workflow) + } + } + + return r0 +} + +// Master_Workflow_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Workflow' +type Master_Workflow_Call struct { + *mock.Call +} + +// Workflow is a helper method to define mock.On call +func (_e *Master_Expecter) Workflow() *Master_Workflow_Call { + return &Master_Workflow_Call{Call: _e.mock.On("Workflow")} +} + +func (_c *Master_Workflow_Call) Run(run func()) *Master_Workflow_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Master_Workflow_Call) Return(_a0 keystore.Workflow) *Master_Workflow_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Master_Workflow_Call) RunAndReturn(run func() keystore.Workflow) *Master_Workflow_Call { + _c.Call.Return(run) + return _c +} + // NewMaster creates a new instance of Master. 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 NewMaster(t interface { diff --git a/core/services/keystore/models.go b/core/services/keystore/models.go index d5eec6802b9..e0b53ef95e4 100644 --- a/core/services/keystore/models.go +++ b/core/services/keystore/models.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/workflowkey" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -158,6 +159,7 @@ type keyRing struct { StarkNet map[string]starkkey.Key Aptos map[string]aptoskey.Key VRF map[string]vrfkey.KeyV2 + Workflow map[string]workflowkey.Key LegacyKeys LegacyKeyStorage } @@ -173,6 +175,7 @@ func newKeyRing() *keyRing { StarkNet: make(map[string]starkkey.Key), Aptos: make(map[string]aptoskey.Key), VRF: make(map[string]vrfkey.KeyV2), + Workflow: make(map[string]workflowkey.Key), } } diff --git a/core/services/keystore/workflow.go b/core/services/keystore/workflow.go new file mode 100644 index 00000000000..2f3ac158681 --- /dev/null +++ b/core/services/keystore/workflow.go @@ -0,0 +1,163 @@ +package keystore + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/workflowkey" +) + +// ErrWorkflowKeyExists describes the error when the workflow key already exists +var ErrWorkflowKeyExists = errors.New("can only have 1 Workflow key") + +type Workflow interface { + Get(id string) (workflowkey.Key, error) + GetAll() ([]workflowkey.Key, error) + Create(ctx context.Context) (workflowkey.Key, error) + Add(ctx context.Context, key workflowkey.Key) error + Delete(ctx context.Context, id string) (workflowkey.Key, error) + Import(ctx context.Context, keyJSON []byte, password string) (workflowkey.Key, error) + Export(id string, password string) ([]byte, error) + EnsureKey(ctx context.Context) error +} + +type workflow struct { + *keyManager +} + +var _ Workflow = &workflow{} + +func newWorkflowKeyStore(km *keyManager) *workflow { + return &workflow{ + km, + } +} + +func (ks *workflow) Get(id string) (workflowkey.Key, error) { + ks.lock.RLock() + defer ks.lock.RUnlock() + if ks.isLocked() { + return workflowkey.Key{}, ErrLocked + } + return ks.getByID(id) +} + +func (ks *workflow) GetAll() (keys []workflowkey.Key, _ error) { + ks.lock.RLock() + defer ks.lock.RUnlock() + if ks.isLocked() { + return nil, ErrLocked + } + for _, key := range ks.keyRing.Workflow { + keys = append(keys, key) + } + return keys, nil +} + +func (ks *workflow) Create(ctx context.Context) (workflowkey.Key, error) { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return workflowkey.Key{}, ErrLocked + } + // Ensure you can only have one Workflow at a time. + if len(ks.keyRing.Workflow) > 0 { + return workflowkey.Key{}, ErrWorkflowKeyExists + } + + key, err := workflowkey.New() + if err != nil { + return workflowkey.Key{}, err + } + return key, ks.safeAddKey(ctx, key) +} + +func (ks *workflow) Add(ctx context.Context, key workflowkey.Key) error { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return ErrLocked + } + if len(ks.keyRing.Workflow) > 0 { + return ErrWorkflowKeyExists + } + return ks.safeAddKey(ctx, key) +} + +func (ks *workflow) Delete(ctx context.Context, id string) (workflowkey.Key, error) { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return workflowkey.Key{}, ErrLocked + } + key, err := ks.getByID(id) + if err != nil { + return workflowkey.Key{}, err + } + + err = ks.safeRemoveKey(ctx, key) + + return key, err +} + +func (ks *workflow) Import(ctx context.Context, keyJSON []byte, password string) (workflowkey.Key, error) { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return workflowkey.Key{}, ErrLocked + } + + key, err := workflowkey.FromEncryptedJSON(keyJSON, password) + if err != nil { + return workflowkey.Key{}, errors.Wrap(err, "WorkflowKeyStore#ImportKey failed to decrypt key") + } + if _, found := ks.keyRing.Workflow[key.ID()]; found { + return workflowkey.Key{}, fmt.Errorf("key with ID %s already exists", key.ID()) + } + return key, ks.keyManager.safeAddKey(ctx, key) +} + +func (ks *workflow) 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) +} + +// EnsureKey verifies whether the Workflow key has been seeded, if not, it creates it. +func (ks *workflow) EnsureKey(ctx context.Context) error { + ks.lock.Lock() + defer ks.lock.Unlock() + if ks.isLocked() { + return ErrLocked + } + + if len(ks.keyRing.Workflow) > 0 { + return nil + } + + key, err := workflowkey.New() + if err != nil { + return err + } + + ks.logger.Infof("Created Workflow key with ID %s", key.ID()) + + return ks.safeAddKey(ctx, key) +} + +func (ks *workflow) getByID(id string) (workflowkey.Key, error) { + key, found := ks.keyRing.Workflow[id] + if !found { + return workflowkey.Key{}, KeyNotFoundError{ID: id, KeyType: "Encryption"} + } + return key, nil +} diff --git a/core/services/keystore/workflow_test.go b/core/services/keystore/workflow_test.go new file mode 100644 index 00000000000..051ebdb76a7 --- /dev/null +++ b/core/services/keystore/workflow_test.go @@ -0,0 +1,178 @@ +package keystore_test + +import ( + "context" + "fmt" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/workflowkey" +) + +func Test_EncryptionKeyStore_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.Workflow() + reset := func() { + ctx := context.Background() // Executed on cleanup + _, err := db.Exec("DELETE FROM encrypted_key_rings") + require.NoError(t, err) + keyStore.ResetXXXTestOnly() + require.NoError(t, keyStore.Unlock(ctx, cltest.Password)) + } + + t.Run("initializes with an empty state", func(t *testing.T) { + defer reset() + keys, err := ks.GetAll() + require.NoError(t, err) + require.Empty(t, keys) + }) + + t.Run("errors when getting non-existent ID", func(t *testing.T) { + defer reset() + _, err := ks.Get("non-existent-id") + require.Error(t, err) + }) + + t.Run("creates a key", func(t *testing.T) { + defer reset() + ctx := testutils.Context(t) + key, err := ks.Create(ctx) + require.NoError(t, err) + retrievedKey, err := ks.Get(key.ID()) + require.NoError(t, err) + require.Equal(t, key, retrievedKey) + + t.Run("prevents creating more than one key", func(t *testing.T) { + ctx := testutils.Context(t) + k, err2 := ks.Create(ctx) + + assert.Zero(t, k) + require.Error(t, err2) + assert.True(t, errors.Is(err2, keystore.ErrWorkflowKeyExists)) + }) + }) + + 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.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) + require.Equal(t, key.ID(), importedKey.ID()) + retrievedKey, err := ks.Get(key.ID()) + require.NoError(t, err) + require.Equal(t, importedKey, retrievedKey) + + t.Run("prevents importing more than one key", func(t *testing.T) { + k, err2 := ks.Import(testutils.Context(t), exportJSON, cltest.Password) + + assert.Zero(t, k) + require.Error(t, err2) + assert.Equal(t, fmt.Sprintf("key with ID %s already exists", key.ID()), err2.Error()) + }) + + t.Run("fails to import malformed key", func(t *testing.T) { + k, err2 := ks.Import(testutils.Context(t), []byte(""), cltest.Password) + + assert.Zero(t, k) + require.Error(t, err2) + }) + + t.Run("fails to export non-existent key", func(t *testing.T) { + exportJSON, err = ks.Export("non-existent", cltest.Password) + + require.Error(t, err) + assert.Empty(t, exportJSON) + }) + }) + + t.Run("adds an externally created key / deletes a key", func(t *testing.T) { + defer reset() + ctx := testutils.Context(t) + newKey, err := workflowkey.New() + require.NoError(t, err) + err = ks.Add(ctx, newKey) + require.NoError(t, err) + keys, err := ks.GetAll() + require.NoError(t, err) + require.Len(t, keys, 1) + _, err = ks.Delete(ctx, newKey.ID()) + require.NoError(t, err) + keys, err = ks.GetAll() + require.NoError(t, err) + require.Empty(t, keys) + _, err = ks.Get(newKey.ID()) + require.Error(t, err) + + t.Run("prevents adding more than one key", func(t *testing.T) { + ctx := testutils.Context(t) + err = ks.Add(ctx, newKey) + require.NoError(t, err) + + err = ks.Add(ctx, newKey) + + require.Error(t, err) + assert.True(t, errors.Is(err, keystore.ErrWorkflowKeyExists)) + }) + + t.Run("fails to delete non-existent key", func(t *testing.T) { + k, err2 := ks.Delete(testutils.Context(t), "non-existent") + + assert.Zero(t, k) + require.Error(t, err2) + }) + }) + + t.Run("adds an externally created key/ensures it already exists", func(t *testing.T) { + defer reset() + ctx := testutils.Context(t) + + newKey, err := workflowkey.New() + require.NoError(t, err) + err = ks.Add(ctx, newKey) + require.NoError(t, err) + + err = keyStore.Workflow().EnsureKey(ctx) + require.NoError(t, err) + keys, err2 := ks.GetAll() + require.NoError(t, err2) + + require.Len(t, keys, 1) + require.Equal(t, newKey.ID(), keys[0].ID()) + require.Equal(t, newKey.PublicKey(), keys[0].PublicKey()) + }) + + t.Run("auto creates a key if it doesn't exists when trying to ensure it already exists", func(t *testing.T) { + defer reset() + ctx := testutils.Context(t) + + keys, err := ks.GetAll() + require.NoError(t, err) + assert.Empty(t, keys) + + err = keyStore.Workflow().EnsureKey(ctx) + require.NoError(t, err) + + keys, err = ks.GetAll() + require.NoError(t, err) + + require.NoError(t, err) + require.Len(t, keys, 1) + }) +} diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go index 7c05ffbe52a..3380b4f1bc5 100644 --- a/core/services/llo/delegate.go +++ b/core/services/llo/delegate.go @@ -35,9 +35,9 @@ type delegate struct { cfg DelegateConfig reportCodecs map[llotypes.ReportFormat]datastreamsllo.ReportCodec - src datastreamsllo.ShouldRetireCache - ds datastreamsllo.DataSource - t services.Service + src datastreamsllo.ShouldRetireCache + ds datastreamsllo.DataSource + telem services.Service oracles []Closer } @@ -109,7 +109,13 @@ func (d *delegate) Start(ctx context.Context) error { if !(len(d.cfg.ContractConfigTrackers) == 1 || len(d.cfg.ContractConfigTrackers) == 2) { return fmt.Errorf("expected either 1 or 2 ContractConfigTrackers, got: %d", len(d.cfg.ContractConfigTrackers)) } + + d.cfg.Logger.Debugw("Starting LLO job", "instances", len(d.cfg.ContractConfigTrackers), "jobName", d.cfg.JobName.ValueOrZero(), "captureEATelemetry", d.cfg.CaptureEATelemetry) + var merr error + + merr = errors.Join(merr, d.telem.Start(ctx)) + psrrc := NewPluginScopedRetirementReportCache(d.cfg.RetirementReportCache, d.cfg.OnchainKeyring, d.cfg.RetirementReportCodec) for i, configTracker := range d.cfg.ContractConfigTrackers { lggr := logger.Named(d.cfg.Logger, fmt.Sprintf("%d", i)) @@ -156,10 +162,11 @@ func (d *delegate) Start(ctx context.Context) error { } func (d *delegate) Close() error { - return d.StopOnce("LLODelegate", func() (err error) { + return d.StopOnce("LLODelegate", func() (merr error) { for _, oracle := range d.oracles { - err = errors.Join(err, oracle.Close()) + merr = errors.Join(merr, oracle.Close()) } - return err + merr = errors.Join(merr, d.telem.Close()) + return merr }) } diff --git a/core/services/llo/mercurytransmitter/persistence_manager.go b/core/services/llo/mercurytransmitter/persistence_manager.go index eb36a7d1b80..ffa82493c9c 100644 --- a/core/services/llo/mercurytransmitter/persistence_manager.go +++ b/core/services/llo/mercurytransmitter/persistence_manager.go @@ -78,7 +78,7 @@ func (pm *persistenceManager) Load(ctx context.Context) ([]*Transmission, error) func (pm *persistenceManager) runFlushDeletesLoop() { defer pm.wg.Done() - ctx, cancel := pm.stopCh.Ctx(context.Background()) + ctx, cancel := pm.stopCh.NewCtx() defer cancel() ticker := services.NewTicker(pm.flushDeletesFrequency) diff --git a/core/services/llo/mercurytransmitter/server.go b/core/services/llo/mercurytransmitter/server.go index 72ff8b669ba..70e76655961 100644 --- a/core/services/llo/mercurytransmitter/server.go +++ b/core/services/llo/mercurytransmitter/server.go @@ -106,7 +106,7 @@ func (s *server) HealthReport() map[string]error { func (s *server) runDeleteQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup) { defer wg.Done() - runloopCtx, cancel := stopCh.Ctx(context.Background()) + ctx, cancel := stopCh.NewCtx() defer cancel() // Exponential backoff for very rarely occurring errors (DB disconnect etc) @@ -121,8 +121,8 @@ func (s *server) runDeleteQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup select { case hash := <-s.deleteQueue: for { - if err := s.pm.orm.Delete(runloopCtx, [][32]byte{hash}); err != nil { - s.lggr.Errorw("Failed to delete transmission record", "err", err, "transmissionHash", fmt.Sprintf("%x", hash)) + if err := s.pm.orm.Delete(ctx, [][32]byte{hash}); err != nil { + s.lggr.Errorw("Failed to delete transmission record", "err", err, "transmissionHash", hash) s.transmitQueueDeleteErrorCount.Inc() select { case <-time.After(b.Duration()): @@ -154,7 +154,7 @@ func (s *server) runQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup, donI Factor: 2, Jitter: true, } - runloopCtx, cancel := stopCh.Ctx(context.Background()) + ctx, cancel := stopCh.NewCtx() defer cancel() for { t := s.q.BlockingPop() @@ -162,12 +162,13 @@ func (s *server) runQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup, donI // queue was closed return } - ctx, cancel := context.WithTimeout(runloopCtx, utils.WithJitter(s.transmitTimeout)) - res, err := s.transmit(ctx, t) - cancel() - if runloopCtx.Err() != nil { - // runloop context is only canceled on transmitter close so we can - // exit the runloop here + res, err := func(ctx context.Context) (*pb.TransmitResponse, error) { + ctx, cancelFn := context.WithTimeout(ctx, utils.WithJitter(s.transmitTimeout)) + defer cancelFn() + return s.transmit(ctx, t) + }(ctx) + if ctx.Err() != nil { + // only canceled on transmitter close so we can exit return } else if err != nil { s.transmitConnectionErrorCount.Inc() diff --git a/core/services/llo/onchain_channel_definition_cache.go b/core/services/llo/onchain_channel_definition_cache.go index 8467a84aaef..3613108d133 100644 --- a/core/services/llo/onchain_channel_definition_cache.go +++ b/core/services/llo/onchain_channel_definition_cache.go @@ -108,7 +108,7 @@ type channelDefinitionCache struct { persistedVersion uint32 wg sync.WaitGroup - chStop chan struct{} + chStop services.StopChan } type HTTPClient interface { @@ -180,7 +180,7 @@ func (c *channelDefinitionCache) Start(ctx context.Context) error { func (c *channelDefinitionCache) pollChainLoop() { defer c.wg.Done() - ctx, cancel := services.StopChan(c.chStop).NewCtx() + ctx, cancel := c.chStop.NewCtx() defer cancel() pollT := services.NewTicker(c.logPollInterval) @@ -353,7 +353,7 @@ func (c *channelDefinitionCache) fetchAndSetChannelDefinitions(ctx context.Conte c.definitionsVersion = log.Version c.definitionsMu.Unlock() - if memoryVersion, persistedVersion, err := c.persist(context.Background()); err != nil { + if memoryVersion, persistedVersion, err := c.persist(ctx); err != nil { // If this fails, the failedPersistLoop will try again c.lggr.Warnw("Failed to persist channel definitions", "err", err, "memoryVersion", memoryVersion, "persistedVersion", persistedVersion) } @@ -457,7 +457,7 @@ func (c *channelDefinitionCache) persist(ctx context.Context) (memoryVersion, pe func (c *channelDefinitionCache) failedPersistLoop() { defer c.wg.Done() - ctx, cancel := services.StopChan(c.chStop).NewCtx() + ctx, cancel := c.chStop.NewCtx() defer cancel() for { diff --git a/core/services/llo/telemetry.go b/core/services/llo/telemetry.go index 62b586f5cc8..d5c113c61ef 100644 --- a/core/services/llo/telemetry.go +++ b/core/services/llo/telemetry.go @@ -61,6 +61,12 @@ type telemeter struct { } func (t *telemeter) EnqueueV3PremiumLegacy(run *pipeline.Run, trrs pipeline.TaskRunResults, streamID uint32, opts llo.DSOpts, val llo.StreamValue, err error) { + if t.Service.Ready() != nil { + // This should never happen, telemeter should always be started BEFORE + // the oracle and closed AFTER it + t.eng.SugaredLogger.Errorw("Telemeter not ready, dropping observation", "run", run, "streamID", streamID, "opts", opts, "val", val, "err", err) + return + } var adapterError *eautils.AdapterError var dpInvariantViolationDetected bool if errors.As(err, &adapterError) && adapterError.Name == adapterLWBAErrorName { diff --git a/core/services/ocr2/plugins/ccip/exportinternal.go b/core/services/ocr2/plugins/ccip/exportinternal.go index aecf1a0b163..6b24cba4857 100644 --- a/core/services/ocr2/plugins/ccip/exportinternal.go +++ b/core/services/ocr2/plugins/ccip/exportinternal.go @@ -38,32 +38,32 @@ func NewEvmPriceRegistry(lp logpoller.LogPoller, ec client.Client, lggr logger.L type VersionFinder = factory.VersionFinder -func NewCommitStoreReader(lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller) (ccipdata.CommitStoreReader, error) { - return factory.NewCommitStoreReader(lggr, versionFinder, address, ec, lp) +func NewCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller) (ccipdata.CommitStoreReader, error) { + return factory.NewCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp) } -func CloseCommitStoreReader(lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller) error { - return factory.CloseCommitStoreReader(lggr, versionFinder, address, ec, lp) +func CloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address ccip.Address, ec client.Client, lp logpoller.LogPoller) error { + return factory.CloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp) } -func NewOffRampReader(lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool) (ccipdata.OffRampReader, error) { - return factory.NewOffRampReader(lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, registerFilters) +func NewOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool) (ccipdata.OffRampReader, error) { + return factory.NewOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, registerFilters) } -func CloseOffRampReader(lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) error { - return factory.CloseOffRampReader(lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice) +func CloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr ccip.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) error { + return factory.CloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice) } func NewEvmVersionFinder() factory.EvmVersionFinder { return factory.NewEvmVersionFinder() } -func NewOnRampReader(lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress ccip.Address, sourceLP logpoller.LogPoller, source client.Client) (ccipdata.OnRampReader, error) { - return factory.NewOnRampReader(lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source) +func NewOnRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress ccip.Address, sourceLP logpoller.LogPoller, source client.Client) (ccipdata.OnRampReader, error) { + return factory.NewOnRampReader(ctx, lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source) } -func CloseOnRampReader(lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress ccip.Address, sourceLP logpoller.LogPoller, source client.Client) error { - return factory.CloseOnRampReader(lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source) +func CloseOnRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress ccip.Address, sourceLP logpoller.LogPoller, source client.Client) error { + return factory.CloseOnRampReader(ctx, lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source) } type OffRampReader = ccipdata.OffRampReader @@ -86,12 +86,12 @@ func NewDynamicLimitedBatchCaller( return rpclib.NewDynamicLimitedBatchCaller(lggr, batchSender, batchSizeLimit, backOffMultiplier, parallelRpcCallsLimit) } -func NewUSDCReader(lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller, registerFilters bool) (*ccipdata.USDCReaderImpl, error) { - return ccipdata.NewUSDCReader(lggr, jobID, transmitter, lp, registerFilters) +func NewUSDCReader(ctx context.Context, lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller, registerFilters bool) (*ccipdata.USDCReaderImpl, error) { + return ccipdata.NewUSDCReader(ctx, lggr, jobID, transmitter, lp, registerFilters) } -func CloseUSDCReader(lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller) error { - return ccipdata.CloseUSDCReader(lggr, jobID, transmitter, lp) +func CloseUSDCReader(ctx context.Context, lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller) error { + return ccipdata.CloseUSDCReader(ctx, lggr, jobID, transmitter, lp) } type USDCReaderImpl = ccipdata.USDCReaderImpl diff --git a/core/services/ocr2/plugins/ccip/internal/cache/chain_health.go b/core/services/ocr2/plugins/ccip/internal/cache/chain_health.go index 00f90615eb2..b029ee02132 100644 --- a/core/services/ocr2/plugins/ccip/internal/cache/chain_health.go +++ b/core/services/ocr2/plugins/ccip/internal/cache/chain_health.go @@ -57,15 +57,12 @@ type chainHealthcheck struct { commitStore ccipdata.CommitStoreReader services.StateMachine - wg *sync.WaitGroup - backgroundCtx context.Context //nolint:containedctx - backgroundCancel context.CancelFunc + wg sync.WaitGroup + stopChan services.StopChan } func NewChainHealthcheck(lggr logger.Logger, onRamp ccipdata.OnRampReader, commitStore ccipdata.CommitStoreReader) *chainHealthcheck { - ctx, cancel := context.WithCancel(context.Background()) - - ch := &chainHealthcheck{ + return &chainHealthcheck{ // Different keys use different expiration times, so we don't need to worry about the default value cache: cache.New(cache.NoExpiration, 0), rmnStatusKey: rmnStatusKey, @@ -76,18 +73,12 @@ func NewChainHealthcheck(lggr logger.Logger, onRamp ccipdata.OnRampReader, commi lggr: lggr, onRamp: onRamp, commitStore: commitStore, - - wg: new(sync.WaitGroup), - backgroundCtx: ctx, - backgroundCancel: cancel, + stopChan: make(services.StopChan), } - return ch } // newChainHealthcheckWithCustomEviction is used for testing purposes only. It doesn't start background worker func newChainHealthcheckWithCustomEviction(lggr logger.Logger, onRamp ccipdata.OnRampReader, commitStore ccipdata.CommitStoreReader, globalStatusDuration time.Duration, rmnStatusRefreshInterval time.Duration) *chainHealthcheck { - ctx, cancel := context.WithCancel(context.Background()) - return &chainHealthcheck{ cache: cache.New(rmnStatusRefreshInterval, 0), rmnStatusKey: rmnStatusKey, @@ -98,10 +89,7 @@ func newChainHealthcheckWithCustomEviction(lggr logger.Logger, onRamp ccipdata.O lggr: lggr, onRamp: onRamp, commitStore: commitStore, - - wg: new(sync.WaitGroup), - backgroundCtx: ctx, - backgroundCancel: cancel, + stopChan: make(services.StopChan), } } @@ -145,7 +133,6 @@ func (c *chainHealthcheck) IsHealthy(ctx context.Context) (bool, error) { func (c *chainHealthcheck) Start(context.Context) error { return c.StateMachine.StartOnce("ChainHealthcheck", func() error { c.lggr.Info("Starting ChainHealthcheck") - c.wg.Add(1) c.run() return nil }) @@ -154,7 +141,7 @@ func (c *chainHealthcheck) Start(context.Context) error { func (c *chainHealthcheck) Close() error { return c.StateMachine.StopOnce("ChainHealthcheck", func() error { c.lggr.Info("Closing ChainHealthcheck") - c.backgroundCancel() + close(c.stopChan) c.wg.Wait() return nil }) @@ -162,17 +149,20 @@ func (c *chainHealthcheck) Close() error { func (c *chainHealthcheck) run() { ticker := time.NewTicker(c.rmnStatusRefreshInterval) + c.wg.Add(1) go func() { defer c.wg.Done() + ctx, cancel := c.stopChan.NewCtx() + defer cancel() // Refresh the RMN state immediately after starting the background refresher - _, _ = c.refresh(c.backgroundCtx) + _, _ = c.refresh(ctx) for { select { - case <-c.backgroundCtx.Done(): + case <-ctx.Done(): return case <-ticker.C: - _, err := c.refresh(c.backgroundCtx) + _, err := c.refresh(ctx) if err != nil { c.lggr.Errorw("Failed to refresh RMN state in the background", "err", err) } 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 f46b1b55b1f..0f234bab8a6 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/commit_store_reader_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/commit_store_reader_test.go @@ -1,7 +1,6 @@ package ccipdata_test import ( - "context" "math/big" "reflect" "testing" @@ -15,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclientmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -180,7 +180,7 @@ func TestCommitStoreReaders(t *testing.T) { ge.On("L1Oracle").Return(lm) maxGasPrice := big.NewInt(1e8) - c12r, err := factory.NewCommitStoreReader(lggr, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(addr2), ec, lp) + c12r, err := factory.NewCommitStoreReader(ctx, lggr, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(addr2), ec, lp) require.NoError(t, err) err = c12r.SetGasEstimator(ctx, ge) require.NoError(t, err) @@ -228,7 +228,7 @@ func TestCommitStoreReaders(t *testing.T) { commitAndGetBlockTs(ec) // Capture all logs. - lp.PollAndSaveLogs(context.Background(), 1) + lp.PollAndSaveLogs(ctx, 1) configs := map[string][][]byte{ ccipdata.V1_2_0: {onchainConfig2, offchainConfig2}, @@ -248,7 +248,7 @@ func TestCommitStoreReaders(t *testing.T) { cr := cr t.Run("CommitStoreReader "+v, func(t *testing.T) { // Static config. - cfg, err := cr.GetCommitStoreStaticConfig(context.Background()) + cfg, err := cr.GetCommitStoreStaticConfig(ctx) require.NoError(t, err) require.NotNil(t, cfg) @@ -260,33 +260,33 @@ func TestCommitStoreReaders(t *testing.T) { assert.Equal(t, d, rep) // Assert reading - latest, err := cr.GetLatestPriceEpochAndRound(context.Background()) + latest, err := cr.GetLatestPriceEpochAndRound(ctx) require.NoError(t, err) assert.Equal(t, er.Uint64(), latest) // Assert cursing - down, err := cr.IsDown(context.Background()) + down, err := cr.IsDown(ctx) require.NoError(t, err) assert.False(t, down) _, err = arm.VoteToCurse(user, [32]byte{}) require.NoError(t, err) ec.Commit() - down, err = cr.IsDown(context.Background()) + down, err = cr.IsDown(ctx) require.NoError(t, err) assert.True(t, down) _, err = arm.OwnerUnvoteToCurse0(user, nil) require.NoError(t, err) ec.Commit() - seqNr, err := cr.GetExpectedNextSequenceNumber(context.Background()) + seqNr, err := cr.GetExpectedNextSequenceNumber(ctx) require.NoError(t, err) assert.Equal(t, rep.Interval.Max+1, seqNr) - reps, err := cr.GetCommitReportMatchingSeqNum(context.Background(), rep.Interval.Max+1, 0) + reps, err := cr.GetCommitReportMatchingSeqNum(ctx, rep.Interval.Max+1, 0) require.NoError(t, err) assert.Len(t, reps, 0) - reps, err = cr.GetCommitReportMatchingSeqNum(context.Background(), rep.Interval.Max, 0) + reps, err = cr.GetCommitReportMatchingSeqNum(ctx, rep.Interval.Max, 0) require.NoError(t, err) require.Len(t, reps, 1) assert.Equal(t, reps[0].Interval, rep.Interval) @@ -294,7 +294,7 @@ func TestCommitStoreReaders(t *testing.T) { assert.Equal(t, reps[0].GasPrices, rep.GasPrices) assert.Equal(t, reps[0].TokenPrices, rep.TokenPrices) - reps, err = cr.GetCommitReportMatchingSeqNum(context.Background(), rep.Interval.Min, 0) + reps, err = cr.GetCommitReportMatchingSeqNum(ctx, rep.Interval.Min, 0) require.NoError(t, err) require.Len(t, reps, 1) assert.Equal(t, reps[0].Interval, rep.Interval) @@ -302,12 +302,12 @@ func TestCommitStoreReaders(t *testing.T) { assert.Equal(t, reps[0].GasPrices, rep.GasPrices) assert.Equal(t, reps[0].TokenPrices, rep.TokenPrices) - reps, err = cr.GetCommitReportMatchingSeqNum(context.Background(), rep.Interval.Min-1, 0) + reps, err = cr.GetCommitReportMatchingSeqNum(ctx, rep.Interval.Min-1, 0) require.NoError(t, err) require.Len(t, reps, 0) // Sanity - reps, err = cr.GetAcceptedCommitReportsGteTimestamp(context.Background(), time.Unix(0, 0), 0) + reps, err = cr.GetAcceptedCommitReportsGteTimestamp(ctx, time.Unix(0, 0), 0) require.NoError(t, err) require.Len(t, reps, 1) assert.Equal(t, reps[0].Interval, rep.Interval) @@ -329,7 +329,7 @@ func TestCommitStoreReaders(t *testing.T) { // We should be able to query for gas prices now. gpe, err := cr.GasPriceEstimator(ctx) require.NoError(t, err) - gp, err := gpe.GetGasPrice(context.Background()) + gp, err := gpe.GetGasPrice(ctx) require.NoError(t, err) assert.True(t, gp.Cmp(big.NewInt(0)) > 0) }) @@ -360,6 +360,7 @@ func TestNewCommitStoreReader(t *testing.T) { } for _, tc := range tt { t.Run(tc.typeAndVersion, func(t *testing.T) { + ctx := tests.Context(t) b, err := utils.ABIEncode(`[{"type":"string"}]`, tc.typeAndVersion) require.NoError(t, err) c := evmclientmocks.NewClient(t) @@ -369,7 +370,7 @@ func TestNewCommitStoreReader(t *testing.T) { if tc.expectedErr == "" { lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil) } - _, err = factory.NewCommitStoreReader(logger.Test(t), factory.NewEvmVersionFinder(), addr, c, lp) + _, err = factory.NewCommitStoreReader(ctx, logger.Test(t), factory.NewEvmVersionFinder(), addr, c, lp) if tc.expectedErr != "" { require.EqualError(t, err, tc.expectedErr) } else { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go index ec4cdded9a7..d9cd523d75e 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go @@ -1,6 +1,8 @@ package factory import ( + "context" + "github.com/Masterminds/semver/v3" "github.com/pkg/errors" @@ -19,16 +21,16 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0" ) -func NewCommitStoreReader(lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) (ccipdata.CommitStoreReader, error) { - return initOrCloseCommitStoreReader(lggr, versionFinder, address, ec, lp, false) +func NewCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) (ccipdata.CommitStoreReader, error) { + return initOrCloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, false) } -func CloseCommitStoreReader(lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) error { - _, err := initOrCloseCommitStoreReader(lggr, versionFinder, address, ec, lp, true) +func CloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) error { + _, err := initOrCloseCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp, true) return err } -func initOrCloseCommitStoreReader(lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller, closeReader bool) (ccipdata.CommitStoreReader, error) { +func initOrCloseCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller, closeReader bool) (ccipdata.CommitStoreReader, error) { contractType, version, err := versionFinder.TypeAndVersion(address, ec) if err != nil { return nil, errors.Wrapf(err, "unable to read type and version") @@ -53,7 +55,7 @@ func initOrCloseCommitStoreReader(lggr logger.Logger, versionFinder VersionFinde if closeReader { return nil, cs.Close() } - return cs, cs.RegisterFilters() + return cs, cs.RegisterFilters(ctx) case ccipdata.V1_5_0: cs, err := v1_5_0.NewCommitStore(lggr, evmAddr, ec, lp) if err != nil { @@ -62,7 +64,7 @@ func initOrCloseCommitStoreReader(lggr logger.Logger, versionFinder VersionFinde if closeReader { return nil, cs.Close() } - return cs, cs.RegisterFilters() + return cs, cs.RegisterFilters(ctx) default: return nil, errors.Errorf("unsupported commit store version %v", version.String()) } diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go index 6beb6953d1a..cd81a0633ce 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/mock" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -20,6 +21,7 @@ import ( ) func TestCommitStore(t *testing.T) { + ctx := tests.Context(t) for _, versionStr := range []string{ccipdata.V1_2_0} { lggr := logger.Test(t) addr := cciptypes.Address(utils.RandomAddress().String()) @@ -27,12 +29,12 @@ func TestCommitStore(t *testing.T) { lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil) versionFinder := newMockVersionFinder(ccipconfig.CommitStore, *semver.MustParse(versionStr), nil) - _, err := NewCommitStoreReader(lggr, versionFinder, addr, nil, lp) + _, err := NewCommitStoreReader(ctx, lggr, versionFinder, addr, nil, lp) assert.NoError(t, err) expFilterName := logpoller.FilterName(v1_2_0.ExecReportAccepts, addr) lp.On("UnregisterFilter", mock.Anything, expFilterName).Return(nil) - err = CloseCommitStoreReader(lggr, versionFinder, addr, nil, lp) + err = CloseCommitStoreReader(ctx, lggr, versionFinder, addr, nil, lp) assert.NoError(t, err) } } diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go index f0f26fb37f3..136079b5b3e 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go @@ -24,16 +24,16 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0" ) -func NewOffRampReader(lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool) (ccipdata.OffRampReader, error) { - return initOrCloseOffRampReader(lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, false, registerFilters) +func NewOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, registerFilters bool) (ccipdata.OffRampReader, error) { + return initOrCloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, false, registerFilters) } -func CloseOffRampReader(lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) error { - _, err := initOrCloseOffRampReader(lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, true, false) +func CloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int) error { + _, err := initOrCloseOffRampReader(ctx, lggr, versionFinder, addr, destClient, lp, estimator, destMaxGasPrice, true, false) return err } -func initOrCloseOffRampReader(lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, closeReader bool, registerFilters bool) (ccipdata.OffRampReader, error) { +func initOrCloseOffRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, addr cciptypes.Address, destClient client.Client, lp logpoller.LogPoller, estimator gas.EvmFeeEstimator, destMaxGasPrice *big.Int, closeReader bool, registerFilters bool) (ccipdata.OffRampReader, error) { contractType, version, err := versionFinder.TypeAndVersion(addr, destClient) if err != nil { return nil, errors.Wrapf(err, "unable to read type and version") @@ -58,7 +58,7 @@ func initOrCloseOffRampReader(lggr logger.Logger, versionFinder VersionFinder, a if closeReader { return nil, offRamp.Close() } - return offRamp, offRamp.RegisterFilters() + return offRamp, offRamp.RegisterFilters(ctx) case ccipdata.V1_5_0: offRamp, err := v1_5_0.NewOffRamp(lggr, evmAddr, destClient, lp, estimator, destMaxGasPrice) if err != nil { @@ -67,7 +67,7 @@ func initOrCloseOffRampReader(lggr logger.Logger, versionFinder VersionFinder, a if closeReader { return nil, offRamp.Close() } - return offRamp, offRamp.RegisterFilters() + return offRamp, offRamp.RegisterFilters(ctx) default: return nil, errors.Errorf("unsupported offramp version %v", version.String()) } diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go index 1851a6fb612..bfb8da5e32c 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp_test.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" mocks2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" @@ -19,6 +20,7 @@ import ( ) func TestOffRamp(t *testing.T) { + ctx := tests.Context(t) for _, versionStr := range []string{ccipdata.V1_2_0} { lggr := logger.Test(t) addr := cciptypes.Address(utils.RandomAddress().String()) @@ -32,13 +34,13 @@ func TestOffRamp(t *testing.T) { versionFinder := newMockVersionFinder(ccipconfig.EVM2EVMOffRamp, *semver.MustParse(versionStr), nil) lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil).Times(len(expFilterNames)) - _, err := NewOffRampReader(lggr, versionFinder, addr, nil, lp, nil, nil, true) + _, err := NewOffRampReader(ctx, lggr, versionFinder, addr, nil, lp, nil, nil, true) assert.NoError(t, err) for _, f := range expFilterNames { lp.On("UnregisterFilter", mock.Anything, f).Return(nil) } - err = CloseOffRampReader(lggr, versionFinder, addr, nil, lp, nil, nil) + err = CloseOffRampReader(ctx, lggr, versionFinder, addr, nil, lp, nil, nil) assert.NoError(t, err) } } diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go index e04a34f72de..57bf6e2eeb3 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go @@ -1,6 +1,8 @@ package factory import ( + "context" + "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -17,16 +19,16 @@ import ( ) // NewOnRampReader determines the appropriate version of the onramp and returns a reader for it -func NewOnRampReader(lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress cciptypes.Address, sourceLP logpoller.LogPoller, source client.Client) (ccipdata.OnRampReader, error) { - return initOrCloseOnRampReader(lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source, false) +func NewOnRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress cciptypes.Address, sourceLP logpoller.LogPoller, source client.Client) (ccipdata.OnRampReader, error) { + return initOrCloseOnRampReader(ctx, lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source, false) } -func CloseOnRampReader(lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress cciptypes.Address, sourceLP logpoller.LogPoller, source client.Client) error { - _, err := initOrCloseOnRampReader(lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source, true) +func CloseOnRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress cciptypes.Address, sourceLP logpoller.LogPoller, source client.Client) error { + _, err := initOrCloseOnRampReader(ctx, lggr, versionFinder, sourceSelector, destSelector, onRampAddress, sourceLP, source, true) return err } -func initOrCloseOnRampReader(lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress cciptypes.Address, sourceLP logpoller.LogPoller, source client.Client, closeReader bool) (ccipdata.OnRampReader, error) { +func initOrCloseOnRampReader(ctx context.Context, lggr logger.Logger, versionFinder VersionFinder, sourceSelector, destSelector uint64, onRampAddress cciptypes.Address, sourceLP logpoller.LogPoller, source client.Client, closeReader bool) (ccipdata.OnRampReader, error) { contractType, version, err := versionFinder.TypeAndVersion(onRampAddress, source) if err != nil { return nil, errors.Wrapf(err, "unable to read type and version") @@ -51,7 +53,7 @@ func initOrCloseOnRampReader(lggr logger.Logger, versionFinder VersionFinder, so if closeReader { return nil, onRamp.Close() } - return onRamp, onRamp.RegisterFilters() + return onRamp, onRamp.RegisterFilters(ctx) case ccipdata.V1_5_0: onRamp, err := v1_5_0.NewOnRamp(lggr, sourceSelector, destSelector, onRampAddrEvm, sourceLP, source) if err != nil { @@ -60,7 +62,7 @@ func initOrCloseOnRampReader(lggr logger.Logger, versionFinder VersionFinder, so if closeReader { return nil, onRamp.Close() } - return onRamp, onRamp.RegisterFilters() + return onRamp, onRamp.RegisterFilters(ctx) // Adding a new version? // Please update the public factory function in leafer.go if the new version updates the leaf hash function. default: diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp_test.go index 320c8d6c301..bc1351f97c9 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp_test.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" mocks2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" @@ -18,6 +19,7 @@ import ( ) func TestOnRamp(t *testing.T) { + ctx := tests.Context(t) for _, versionStr := range []string{ccipdata.V1_2_0, ccipdata.V1_5_0} { lggr := logger.Test(t) addr := cciptypes.Address(utils.RandomAddress().String()) @@ -33,13 +35,13 @@ func TestOnRamp(t *testing.T) { versionFinder := newMockVersionFinder(ccipconfig.EVM2EVMOnRamp, *semver.MustParse(versionStr), nil) lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil).Times(len(expFilterNames)) - _, err := NewOnRampReader(lggr, versionFinder, sourceSelector, destSelector, addr, lp, nil) + _, err := NewOnRampReader(ctx, lggr, versionFinder, sourceSelector, destSelector, addr, lp, nil) assert.NoError(t, err) for _, f := range expFilterNames { lp.On("UnregisterFilter", mock.Anything, f).Return(nil) } - err = CloseOnRampReader(lggr, versionFinder, sourceSelector, destSelector, addr, lp, nil) + err = CloseOnRampReader(ctx, lggr, versionFinder, sourceSelector, destSelector, addr, lp, nil) assert.NoError(t, err) } } diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go index cb82e7273bf..90a40eee1a5 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go @@ -43,7 +43,7 @@ func initOrClosePriceRegistryReader(ctx context.Context, lggr logger.Logger, ver } switch version.String() { case ccipdata.V1_2_0: - pr, err := v1_2_0.NewPriceRegistry(lggr, priceRegistryEvmAddr, lp, cl, registerFilters) + pr, err := v1_2_0.NewPriceRegistry(ctx, lggr, priceRegistryEvmAddr, lp, cl, registerFilters) if err != nil { return nil, err } @@ -52,7 +52,7 @@ func initOrClosePriceRegistryReader(ctx context.Context, lggr logger.Logger, ver } return pr, nil case ccipdata.V1_6_0: - pr, err := v1_2_0.NewPriceRegistry(lggr, priceRegistryEvmAddr, lp, cl, registerFilters) + pr, err := v1_2_0.NewPriceRegistry(ctx, lggr, priceRegistryEvmAddr, lp, cl, registerFilters) if err != nil { return nil, err } 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 d0b3fe53436..17f9bcfb370 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 @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclientmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -139,7 +140,7 @@ func setupOffRampReaderTH(t *testing.T, version string) offRampReaderTH { } // Create the version-specific reader. - reader, err := factory.NewOffRampReader(log, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(offRampAddress), bc, lp, nil, nil, true) + reader, err := factory.NewOffRampReader(ctx, log, factory.NewEvmVersionFinder(), ccipcalc.EvmAddrToGeneric(offRampAddress), bc, lp, nil, nil, true) require.NoError(t, err) addr, err := reader.Address(ctx) require.NoError(t, err) @@ -306,6 +307,7 @@ func TestNewOffRampReader(t *testing.T) { } for _, tc := range tt { t.Run(tc.typeAndVersion, func(t *testing.T) { + ctx := tests.Context(t) b, err := utils.ABIEncode(`[{"type":"string"}]`, tc.typeAndVersion) require.NoError(t, err) c := evmclientmocks.NewClient(t) @@ -313,7 +315,7 @@ func TestNewOffRampReader(t *testing.T) { addr := ccipcalc.EvmAddrToGeneric(utils.RandomAddress()) lp := lpmocks.NewLogPoller(t) lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil).Maybe() - _, err = factory.NewOffRampReader(logger.Test(t), factory.NewEvmVersionFinder(), addr, c, lp, nil, nil, true) + _, err = factory.NewOffRampReader(ctx, logger.Test(t), factory.NewEvmVersionFinder(), addr, c, lp, nil, nil, true) if tc.expectedErr != "" { assert.EqualError(t, err, tc.expectedErr) } else { 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 db2e54f96ba..6340eb21682 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 @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -37,9 +38,10 @@ type onRampReaderTH struct { } func TestNewOnRampReader_noContractAtAddress(t *testing.T) { + ctx := tests.Context(t) _, bc := ccipdata.NewSimulation(t) addr := ccipcalc.EvmAddrToGeneric(utils.RandomAddress()) - _, err := factory.NewOnRampReader(logger.Test(t), factory.NewEvmVersionFinder(), testutils.SimulatedChainID.Uint64(), testutils.SimulatedChainID.Uint64(), addr, lpmocks.NewLogPoller(t), bc) + _, err := factory.NewOnRampReader(ctx, logger.Test(t), factory.NewEvmVersionFinder(), testutils.SimulatedChainID.Uint64(), testutils.SimulatedChainID.Uint64(), addr, lpmocks.NewLogPoller(t), bc) assert.EqualError(t, err, fmt.Sprintf("unable to read type and version: error calling typeAndVersion on addr: %s no contract code at given address", addr)) } @@ -67,6 +69,7 @@ func TestOnRampReaderInit(t *testing.T) { } func setupOnRampReaderTH(t *testing.T, version string) onRampReaderTH { + ctx := tests.Context(t) user, bc := ccipdata.NewSimulation(t) log := logger.Test(t) orm := logpoller.NewORM(testutils.SimulatedChainID, pgtest.NewSqlxDB(t), log) @@ -100,7 +103,7 @@ func setupOnRampReaderTH(t *testing.T, version string) onRampReaderTH { } // Create the version-specific reader. - reader, err := factory.NewOnRampReader(log, factory.NewEvmVersionFinder(), testutils.SimulatedChainID.Uint64(), testutils.SimulatedChainID.Uint64(), ccipcalc.EvmAddrToGeneric(onRampAddress), lp, bc) + reader, err := factory.NewOnRampReader(ctx, log, factory.NewEvmVersionFinder(), testutils.SimulatedChainID.Uint64(), testutils.SimulatedChainID.Uint64(), ccipcalc.EvmAddrToGeneric(onRampAddress), lp, bc) require.NoError(t, err) return onRampReaderTH{ @@ -309,6 +312,7 @@ func TestNewOnRampReader(t *testing.T) { } for _, tc := range tt { t.Run(tc.typeAndVersion, func(t *testing.T) { + ctx := tests.Context(t) b, err := utils.ABIEncode(`[{"type":"string"}]`, tc.typeAndVersion) require.NoError(t, err) c := evmclientmocks.NewClient(t) @@ -316,7 +320,7 @@ func TestNewOnRampReader(t *testing.T) { addr := ccipcalc.EvmAddrToGeneric(utils.RandomAddress()) lp := lpmocks.NewLogPoller(t) lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil).Maybe() - _, err = factory.NewOnRampReader(logger.Test(t), factory.NewEvmVersionFinder(), 1, 2, addr, lp, c) + _, err = factory.NewOnRampReader(ctx, logger.Test(t), factory.NewEvmVersionFinder(), 1, 2, addr, lp, c) if tc.expectedErr != "" { require.EqualError(t, err, tc.expectedErr) } else { 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 1f8d48ddfee..f5b97926b6e 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 @@ -2,6 +2,7 @@ package ccipdata_test import ( "context" + "math" "math/big" "reflect" "testing" @@ -137,7 +138,7 @@ func setupPriceRegistryReaderTH(t *testing.T) priceRegReaderTH { b2 := commitAndGetBlockTs(ec) // Capture all lp data. - lp.PollAndSaveLogs(context.Background(), 1) + lp.PollAndSaveLogs(ctx, 1) return priceRegReaderTH{ lp: lp, @@ -162,15 +163,16 @@ func setupPriceRegistryReaderTH(t *testing.T) priceRegReaderTH { } func testPriceRegistryReader(t *testing.T, th priceRegReaderTH, pr ccipdata.PriceRegistryReader) { + ctx := testutils.Context(t) // Assert have expected fee tokens. - gotFeeTokens, err := pr.GetFeeTokens(context.Background()) + gotFeeTokens, err := pr.GetFeeTokens(ctx) require.NoError(t, err) evmAddrs, err := ccipcalc.GenericAddrsToEvm(gotFeeTokens...) require.NoError(t, err) assert.Equal(t, th.expectedFeeTokens, evmAddrs) // Note unsupported chain selector simply returns an empty set not an error - gasUpdates, err := pr.GetGasPriceUpdatesCreatedAfter(context.Background(), 1e6, time.Unix(0, 0), 0) + gasUpdates, err := pr.GetGasPriceUpdatesCreatedAfter(ctx, 1e6, time.Unix(0, 0), 0) require.NoError(t, err) assert.Len(t, gasUpdates, 0) @@ -188,26 +190,30 @@ func testPriceRegistryReader(t *testing.T, th priceRegReaderTH, pr ccipdata.Pric } expectedToken = append(expectedToken, th.expectedTokenUpdates[th.blockTs[j]]...) } - gasUpdates, err = pr.GetAllGasPriceUpdatesCreatedAfter(context.Background(), time.Unix(int64(ts-1), 0), 0) + if ts > math.MaxInt64 { + t.Fatalf("timestamp overflows int64: %d", ts) + } + unixTS := time.Unix(int64(ts-1), 0) //nolint:gosec // G115 false positive + gasUpdates, err = pr.GetAllGasPriceUpdatesCreatedAfter(ctx, unixTS, 0) require.NoError(t, err) assert.Len(t, gasUpdates, len(expectedGas)) - gasUpdates, err = pr.GetGasPriceUpdatesCreatedAfter(context.Background(), th.destSelectors[0], time.Unix(int64(ts-1), 0), 0) + gasUpdates, err = pr.GetGasPriceUpdatesCreatedAfter(ctx, th.destSelectors[0], unixTS, 0) require.NoError(t, err) assert.Len(t, gasUpdates, len(expectedDest0Gas)) - tokenUpdates, err2 := pr.GetTokenPriceUpdatesCreatedAfter(context.Background(), time.Unix(int64(ts-1), 0), 0) + tokenUpdates, err2 := pr.GetTokenPriceUpdatesCreatedAfter(ctx, unixTS, 0) require.NoError(t, err2) assert.Len(t, tokenUpdates, len(expectedToken)) } // Empty token set should return empty set no error. - gotEmpty, err := pr.GetTokenPrices(context.Background(), []cciptypes.Address{}) + gotEmpty, err := pr.GetTokenPrices(ctx, []cciptypes.Address{}) require.NoError(t, err) assert.Len(t, gotEmpty, 0) // We expect latest token prices to apply - allTokenUpdates, err := pr.GetTokenPriceUpdatesCreatedAfter(context.Background(), time.Unix(0, 0), 0) + allTokenUpdates, err := pr.GetTokenPriceUpdatesCreatedAfter(ctx, time.Unix(0, 0), 0) require.NoError(t, err) // Build latest map latest := make(map[cciptypes.Address]*big.Int) @@ -222,7 +228,7 @@ func testPriceRegistryReader(t *testing.T, th priceRegReaderTH, pr ccipdata.Pric latest[allTokenUpdates[i].Token] = allTokenUpdates[i].Value allTokens = append(allTokens, allTokenUpdates[i].Token) } - tokenPrices, err := pr.GetTokenPrices(context.Background(), allTokens) + tokenPrices, err := pr.GetTokenPrices(ctx, allTokens) require.NoError(t, err) require.Len(t, tokenPrices, len(allTokens)) for _, p := range tokenPrices { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go index cd8fd3150ae..792e2eb7253 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go @@ -52,13 +52,11 @@ type USDCReaderImpl struct { } func (u *USDCReaderImpl) Close() error { - // FIXME Dim pgOpts removed from LogPoller return u.lp.UnregisterFilter(context.Background(), u.filter.Name) } -func (u *USDCReaderImpl) RegisterFilters() error { - // FIXME Dim pgOpts removed from LogPoller - return u.lp.RegisterFilter(context.Background(), u.filter) +func (u *USDCReaderImpl) RegisterFilters(ctx context.Context) error { + return u.lp.RegisterFilter(ctx, u.filter) } // usdcPayload has to match the onchain event emitted by the USDC message transmitter @@ -136,7 +134,7 @@ func (u *USDCReaderImpl) GetUSDCMessagePriorToLogIndexInTx(ctx context.Context, return parseUSDCMessageSent(allUsdcTokensData[usdcTokenIndex]) } -func NewUSDCReader(lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller, registerFilters bool) (*USDCReaderImpl, error) { +func NewUSDCReader(ctx context.Context, lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller, registerFilters bool) (*USDCReaderImpl, error) { eventSig := utils.Keccak256Fixed([]byte("MessageSent(bytes)")) r := &USDCReaderImpl{ @@ -154,15 +152,15 @@ func NewUSDCReader(lggr logger.Logger, jobID string, transmitter common.Address, } if registerFilters { - if err := r.RegisterFilters(); err != nil { + if err := r.RegisterFilters(ctx); err != nil { return nil, fmt.Errorf("register filters: %w", err) } } return r, nil } -func CloseUSDCReader(lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller) error { - r, err := NewUSDCReader(lggr, jobID, transmitter, lp, false) +func CloseUSDCReader(ctx context.Context, lggr logger.Logger, jobID string, transmitter common.Address, lp logpoller.LogPoller) error { + r, err := NewUSDCReader(ctx, lggr, jobID, transmitter, lp, false) if err != nil { return err } 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 953da52713b..d3df9e2124a 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 @@ -1,7 +1,6 @@ package ccipdata import ( - "context" "fmt" "testing" "time" @@ -15,6 +14,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "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" @@ -35,8 +35,9 @@ func TestLogPollerClient_GetUSDCMessagePriorToLogIndexInTx(t *testing.T) { lggr := logger.Test(t) t.Run("multiple found - selected last", func(t *testing.T) { + ctx := tests.Context(t) lp := lpmocks.NewLogPoller(t) - u, _ := NewUSDCReader(lggr, "job_123", utils.RandomAddress(), lp, false) + u, _ := NewUSDCReader(ctx, lggr, "job_123", utils.RandomAddress(), lp, false) lp.On("IndexedLogsByTxHash", mock.Anything, @@ -49,15 +50,16 @@ func TestLogPollerClient_GetUSDCMessagePriorToLogIndexInTx(t *testing.T) { {LogIndex: ccipLogIndex, Data: []byte("0")}, {LogIndex: ccipLogIndex + 1, Data: []byte("1")}, }, nil) - usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, 0, txHash.String()) + usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(ctx, ccipLogIndex, 0, txHash.String()) assert.NoError(t, err) assert.Equal(t, expectedPostParse, hexutil.Encode(usdcMessageData)) lp.AssertExpectations(t) }) t.Run("multiple found - selected first", func(t *testing.T) { + ctx := tests.Context(t) lp := lpmocks.NewLogPoller(t) - u, _ := NewUSDCReader(lggr, "job_123", utils.RandomAddress(), lp, false) + u, _ := NewUSDCReader(ctx, lggr, "job_123", utils.RandomAddress(), lp, false) lp.On("IndexedLogsByTxHash", mock.Anything, @@ -70,15 +72,16 @@ func TestLogPollerClient_GetUSDCMessagePriorToLogIndexInTx(t *testing.T) { {LogIndex: ccipLogIndex, Data: []byte("0")}, {LogIndex: ccipLogIndex + 1, Data: []byte("1")}, }, nil) - usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, 1, txHash.String()) + usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(ctx, ccipLogIndex, 1, txHash.String()) assert.NoError(t, err) assert.Equal(t, expectedPostParse, hexutil.Encode(usdcMessageData)) lp.AssertExpectations(t) }) t.Run("logs fetched from memory in subsequent calls", func(t *testing.T) { + ctx := tests.Context(t) lp := lpmocks.NewLogPoller(t) - u, _ := NewUSDCReader(lggr, "job_123", utils.RandomAddress(), lp, false) + u, _ := NewUSDCReader(ctx, lggr, "job_123", utils.RandomAddress(), lp, false) lp.On("IndexedLogsByTxHash", mock.Anything, @@ -93,12 +96,12 @@ func TestLogPollerClient_GetUSDCMessagePriorToLogIndexInTx(t *testing.T) { }, nil).Once() // first call logs must be fetched from lp - usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, 1, txHash.String()) + usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(ctx, ccipLogIndex, 1, txHash.String()) assert.NoError(t, err) assert.Equal(t, expectedPostParse, hexutil.Encode(usdcMessageData)) // subsequent call, logs must be fetched from memory - usdcMessageData, err = u.GetUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, 1, txHash.String()) + usdcMessageData, err = u.GetUSDCMessagePriorToLogIndexInTx(ctx, ccipLogIndex, 1, txHash.String()) assert.NoError(t, err) assert.Equal(t, expectedPostParse, hexutil.Encode(usdcMessageData)) @@ -106,8 +109,9 @@ func TestLogPollerClient_GetUSDCMessagePriorToLogIndexInTx(t *testing.T) { }) t.Run("none found", func(t *testing.T) { + ctx := tests.Context(t) lp := lpmocks.NewLogPoller(t) - u, _ := NewUSDCReader(lggr, "job_123", utils.RandomAddress(), lp, false) + u, _ := NewUSDCReader(ctx, lggr, "job_123", utils.RandomAddress(), lp, false) lp.On("IndexedLogsByTxHash", mock.Anything, u.usdcMessageSent, @@ -115,7 +119,7 @@ func TestLogPollerClient_GetUSDCMessagePriorToLogIndexInTx(t *testing.T) { txHash, ).Return([]logpoller.Log{}, nil) - usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(context.Background(), ccipLogIndex, 0, txHash.String()) + usdcMessageData, err := u.GetUSDCMessagePriorToLogIndexInTx(ctx, ccipLogIndex, 0, txHash.String()) assert.Errorf(t, err, fmt.Sprintf("no USDC message found prior to log index %d in tx %s", ccipLogIndex, txHash.Hex())) assert.Nil(t, usdcMessageData) @@ -137,6 +141,7 @@ func TestParse(t *testing.T) { func TestFilters(t *testing.T) { t.Run("filters of different jobs should be distinct", func(t *testing.T) { + ctx := tests.Context(t) lggr := logger.Test(t) chainID := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) @@ -163,15 +168,15 @@ func TestFilters(t *testing.T) { f1 := logpoller.FilterName("USDC message sent", jobID1, transmitter.Hex()) f2 := logpoller.FilterName("USDC message sent", jobID2, transmitter.Hex()) - _, err := NewUSDCReader(lggr, jobID1, transmitter, lp, true) + _, err := NewUSDCReader(ctx, lggr, jobID1, transmitter, lp, true) assert.NoError(t, err) assert.True(t, lp.HasFilter(f1)) - _, err = NewUSDCReader(lggr, jobID2, transmitter, lp, true) + _, err = NewUSDCReader(ctx, lggr, jobID2, transmitter, lp, true) assert.NoError(t, err) assert.True(t, lp.HasFilter(f2)) - err = CloseUSDCReader(lggr, jobID2, transmitter, lp) + err = CloseUSDCReader(ctx, lggr, jobID2, transmitter, lp) assert.NoError(t, err) assert.True(t, lp.HasFilter(f1)) assert.False(t, lp.HasFilter(f2)) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go index 29076e6cd74..2d772e3bd0a 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go @@ -277,7 +277,7 @@ func (c *CommitStore) ChangeConfig(_ context.Context, onchainConfig []byte, offc } func (c *CommitStore) Close() error { - return logpollerutil.UnregisterLpFilters(c.lp, c.filters) + return logpollerutil.UnregisterLpFilters(context.Background(), c.lp, c.filters) } func (c *CommitStore) parseReport(log types.Log) (*cciptypes.CommitStoreReport, error) { @@ -429,8 +429,8 @@ func (c *CommitStore) VerifyExecutionReport(ctx context.Context, report cciptype return true, nil } -func (c *CommitStore) RegisterFilters() error { - return logpollerutil.RegisterLpFilters(c.lp, c.filters) +func (c *CommitStore) RegisterFilters(ctx context.Context) error { + return logpollerutil.RegisterLpFilters(ctx, c.lp, c.filters) } func NewCommitStore(lggr logger.Logger, addr common.Address, ec client.Client, lp logpoller.LogPoller) (*CommitStore, error) { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go index f2887688965..e8017016690 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go @@ -428,10 +428,10 @@ func (o *OffRamp) ChangeConfig(ctx context.Context, onchainConfigBytes []byte, o } func (o *OffRamp) Close() error { - return logpollerutil.UnregisterLpFilters(o.lp, o.filters) + return logpollerutil.UnregisterLpFilters(context.Background(), o.lp, o.filters) } -func (o *OffRamp) RegisterFilters() error { - return logpollerutil.RegisterLpFilters(o.lp, o.filters) +func (o *OffRamp) RegisterFilters(ctx context.Context) error { + return logpollerutil.RegisterLpFilters(ctx, o.lp, o.filters) } func (o *OffRamp) GetExecutionState(ctx context.Context, sequenceNumber uint64) (uint8, error) { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go index 071e8a8e03e..52f241a30a6 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go @@ -213,11 +213,11 @@ func (o *OnRamp) IsSourceCursed(ctx context.Context) (bool, error) { } func (o *OnRamp) Close() error { - return logpollerutil.UnregisterLpFilters(o.lp, o.filters) + return logpollerutil.UnregisterLpFilters(context.Background(), o.lp, o.filters) } -func (o *OnRamp) RegisterFilters() error { - return logpollerutil.RegisterLpFilters(o.lp, o.filters) +func (o *OnRamp) RegisterFilters(ctx context.Context) error { + return logpollerutil.RegisterLpFilters(ctx, o.lp, o.filters) } func (o *OnRamp) logToMessage(log types.Log) (*cciptypes.EVM2EVMMessage, error) { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go index 4c4058922dc..636b37c9100 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go @@ -50,7 +50,7 @@ type PriceRegistry struct { tokenDecimalsCache sync.Map } -func NewPriceRegistry(lggr logger.Logger, priceRegistryAddr common.Address, lp logpoller.LogPoller, ec client.Client, registerFilters bool) (*PriceRegistry, error) { +func NewPriceRegistry(ctx context.Context, lggr logger.Logger, priceRegistryAddr common.Address, lp logpoller.LogPoller, ec client.Client, registerFilters bool) (*PriceRegistry, error) { priceRegistry, err := price_registry_1_2_0.NewPriceRegistry(priceRegistryAddr, ec) if err != nil { return nil, err @@ -79,7 +79,7 @@ func NewPriceRegistry(lggr logger.Logger, priceRegistryAddr common.Address, lp l Retention: ccipdata.CacheEvictionLogsRetention, }} if registerFilters { - err = logpollerutil.RegisterLpFilters(lp, filters) + err = logpollerutil.RegisterLpFilters(ctx, lp, filters) if err != nil { return nil, err } @@ -151,7 +151,7 @@ func (p *PriceRegistry) GetFeeTokens(ctx context.Context) ([]cciptypes.Address, } func (p *PriceRegistry) Close() error { - return logpollerutil.UnregisterLpFilters(p.lp, p.filters) + return logpollerutil.UnregisterLpFilters(context.Background(), p.lp, p.filters) } func (p *PriceRegistry) GetTokenPriceUpdatesCreatedAfter(ctx context.Context, ts time.Time, confs int) ([]cciptypes.TokenPriceUpdateWithTxMeta, error) { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go index ad540ffd648..da41d116bc8 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go @@ -216,11 +216,11 @@ func (o *OnRamp) IsSourceCursed(ctx context.Context) (bool, error) { } func (o *OnRamp) Close() error { - return logpollerutil.UnregisterLpFilters(o.lp, o.filters) + return logpollerutil.UnregisterLpFilters(context.Background(), o.lp, o.filters) } -func (o *OnRamp) RegisterFilters() error { - return logpollerutil.RegisterLpFilters(o.lp, o.filters) +func (o *OnRamp) RegisterFilters(ctx context.Context) error { + return logpollerutil.RegisterLpFilters(ctx, o.lp, o.filters) } func (o *OnRamp) logToMessage(log types.Log) (*cciptypes.EVM2EVMMessage, error) { diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service.go b/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service.go index e8b9a4de721..b5e8853d67c 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service.go @@ -68,10 +68,9 @@ type priceService struct { destPriceRegistryReader ccipdata.PriceRegistryReader services.StateMachine - wg *sync.WaitGroup - backgroundCtx context.Context //nolint:containedctx - backgroundCancel context.CancelFunc - dynamicConfigMu *sync.RWMutex + wg sync.WaitGroup + stopChan services.StopChan + dynamicConfigMu sync.RWMutex } func NewPriceService( @@ -85,8 +84,6 @@ func NewPriceService( priceGetter pricegetter.AllTokensPriceGetter, offRampReader ccipdata.OffRampReader, ) PriceService { - ctx, cancel := context.WithCancel(context.Background()) - pw := &priceService{ gasUpdateInterval: gasPriceUpdateInterval, tokenUpdateInterval: tokenPriceUpdateInterval, @@ -100,11 +97,7 @@ func NewPriceService( sourceNative: sourceNative, priceGetter: priceGetter, offRampReader: offRampReader, - - wg: new(sync.WaitGroup), - backgroundCtx: ctx, - backgroundCancel: cancel, - dynamicConfigMu: &sync.RWMutex{}, + stopChan: make(services.StopChan), } return pw } @@ -121,13 +114,16 @@ func (p *priceService) Start(context.Context) error { func (p *priceService) Close() error { return p.StateMachine.StopOnce("PriceService", func() error { p.lggr.Info("Closing PriceService") - p.backgroundCancel() + close(p.stopChan) p.wg.Wait() return nil }) } func (p *priceService) run() { + ctx, cancel := p.stopChan.NewCtx() + defer cancel() + gasUpdateTicker := time.NewTicker(utils.WithJitter(p.gasUpdateInterval)) tokenUpdateTicker := time.NewTicker(utils.WithJitter(p.tokenUpdateInterval)) @@ -138,15 +134,15 @@ func (p *priceService) run() { for { select { - case <-p.backgroundCtx.Done(): + case <-ctx.Done(): return case <-gasUpdateTicker.C: - err := p.runGasPriceUpdate(p.backgroundCtx) + err := p.runGasPriceUpdate(ctx) if err != nil { p.lggr.Errorw("Error when updating gas prices in the background", "err", err) } case <-tokenUpdateTicker.C: - err := p.runTokenPriceUpdate(p.backgroundCtx) + err := p.runTokenPriceUpdate(ctx) if err != nil { p.lggr.Errorw("Error when updating token prices in the background", "err", err) } diff --git a/core/services/ocr2/plugins/ccip/internal/logpollerutil/filters.go b/core/services/ocr2/plugins/ccip/internal/logpollerutil/filters.go index e42dd8c154d..de185611641 100644 --- a/core/services/ocr2/plugins/ccip/internal/logpollerutil/filters.go +++ b/core/services/ocr2/plugins/ccip/internal/logpollerutil/filters.go @@ -9,26 +9,24 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" ) -func RegisterLpFilters(lp logpoller.LogPoller, filters []logpoller.Filter) error { +func RegisterLpFilters(ctx context.Context, lp logpoller.LogPoller, filters []logpoller.Filter) error { for _, lpFilter := range filters { if filterContainsZeroAddress(lpFilter.Addresses) { continue } - // FIXME Dim pgOpts removed from LogPoller - if err := lp.RegisterFilter(context.Background(), lpFilter); err != nil { + if err := lp.RegisterFilter(ctx, lpFilter); err != nil { return err } } return nil } -func UnregisterLpFilters(lp logpoller.LogPoller, filters []logpoller.Filter) error { +func UnregisterLpFilters(ctx context.Context, lp logpoller.LogPoller, filters []logpoller.Filter) error { for _, lpFilter := range filters { if filterContainsZeroAddress(lpFilter.Addresses) { continue } - // FIXME Dim pgOpts removed from LogPoller - if err := lp.UnregisterFilter(context.Background(), lpFilter.Name); err != nil { + if err := lp.UnregisterFilter(ctx, lpFilter.Name); err != nil { return err } } diff --git a/core/services/ocr2/plugins/ccip/internal/oraclelib/backfilled_oracle.go b/core/services/ocr2/plugins/ccip/internal/oraclelib/backfilled_oracle.go index d2851e3a079..053cddabcd9 100644 --- a/core/services/ocr2/plugins/ccip/internal/oraclelib/backfilled_oracle.go +++ b/core/services/ocr2/plugins/ccip/internal/oraclelib/backfilled_oracle.go @@ -6,128 +6,23 @@ import ( "sync/atomic" "time" + commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/services" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" ) -type BackfilledOracle struct { - srcStartBlock, dstStartBlock uint64 - oracleStarted atomic.Bool - cancelFn context.CancelFunc - src, dst logpoller.LogPoller - oracle job.ServiceCtx - lggr logger.Logger -} - -func NewBackfilledOracle(lggr logger.Logger, src, dst logpoller.LogPoller, srcStartBlock, dstStartBlock uint64, oracle job.ServiceCtx) *BackfilledOracle { - return &BackfilledOracle{ - srcStartBlock: srcStartBlock, - dstStartBlock: dstStartBlock, - oracleStarted: atomic.Bool{}, - cancelFn: nil, - src: src, - dst: dst, - oracle: oracle, - lggr: lggr, - } -} - -func (r *BackfilledOracle) Start(_ context.Context) error { - go r.Run() - return nil -} - -func (r *BackfilledOracle) IsRunning() bool { - return r.oracleStarted.Load() -} - -func (r *BackfilledOracle) Run() { - ctx, cancelFn := context.WithCancel(context.Background()) - r.cancelFn = cancelFn - var err error - var errMu sync.Mutex - var wg sync.WaitGroup - // Replay in parallel if both requested. - if r.srcStartBlock != 0 { - wg.Add(1) - go func() { - defer wg.Done() - s := time.Now() - r.lggr.Infow("start replaying src chain", "fromBlock", r.srcStartBlock) - srcReplayErr := r.src.Replay(ctx, int64(r.srcStartBlock)) - errMu.Lock() - err = multierr.Combine(err, srcReplayErr) - errMu.Unlock() - r.lggr.Infow("finished replaying src chain", "time", time.Since(s)) - }() - } - if r.dstStartBlock != 0 { - wg.Add(1) - go func() { - defer wg.Done() - s := time.Now() - r.lggr.Infow("start replaying dst chain", "fromBlock", r.dstStartBlock) - dstReplayErr := r.dst.Replay(ctx, int64(r.dstStartBlock)) - errMu.Lock() - err = multierr.Combine(err, dstReplayErr) - errMu.Unlock() - r.lggr.Infow("finished replaying dst chain", "time", time.Since(s)) - }() - } - wg.Wait() - if err != nil { - r.lggr.Criticalw("unexpected error replaying, continuing plugin boot without all the logs backfilled", "err", err) - } - if err := ctx.Err(); err != nil { - r.lggr.Errorw("context already cancelled", "err", err) - return - } - // Start oracle with all logs present from dstStartBlock on dst and - // all logs from srcStartBlock on src. - if err := r.oracle.Start(ctx); err != nil { - // Should never happen. - r.lggr.Errorw("unexpected error starting oracle", "err", err) - } else { - r.oracleStarted.Store(true) - } -} - -func (r *BackfilledOracle) Close() error { - if r.oracleStarted.Load() { - // If the oracle is running, it must be Closed/stopped - if err := r.oracle.Close(); err != nil { - r.lggr.Errorw("unexpected error stopping oracle", "err", err) - return err - } - // Flag the oracle as closed with our internal variable that keeps track - // of its state. This will allow to re-start the process - r.oracleStarted.Store(false) - } - if r.cancelFn != nil { - // This is useful to step the previous tasks that are spawned in - // parallel before starting the Oracle. This will use the context to - // signal them to exit immediately. - // - // It can be possible this is the only way to stop the Start() async - // flow, specially when the previusly task are running (the replays) and - // `oracleStarted` would be false in that example. Calling `cancelFn()` - // will stop the replays and will prevent the oracle to start - r.cancelFn() - } - return nil -} - func NewChainAgnosticBackFilledOracle(lggr logger.Logger, srcProvider services.ServiceCtx, dstProvider services.ServiceCtx, oracle job.ServiceCtx) *ChainAgnosticBackFilledOracle { return &ChainAgnosticBackFilledOracle{ srcProvider: srcProvider, dstProvider: dstProvider, oracle: oracle, lggr: lggr, + stopCh: make(chan struct{}), + done: make(chan struct{}), } } @@ -137,7 +32,8 @@ type ChainAgnosticBackFilledOracle struct { oracle job.ServiceCtx lggr logger.Logger oracleStarted atomic.Bool - cancelFn context.CancelFunc + stopCh commonservices.StopChan + done chan struct{} } func (r *ChainAgnosticBackFilledOracle) Start(_ context.Context) error { @@ -146,8 +42,10 @@ func (r *ChainAgnosticBackFilledOracle) Start(_ context.Context) error { } func (r *ChainAgnosticBackFilledOracle) run() { - ctx, cancelFn := context.WithCancel(context.Background()) - r.cancelFn = cancelFn + defer close(r.done) + ctx, cancel := r.stopCh.NewCtx() + defer cancel() + var err error var errMu sync.Mutex var wg sync.WaitGroup @@ -192,6 +90,8 @@ func (r *ChainAgnosticBackFilledOracle) run() { } func (r *ChainAgnosticBackFilledOracle) Close() error { + close(r.stopCh) + <-r.done if r.oracleStarted.Load() { // If the oracle is running, it must be Closed/stopped // TODO: Close should be safe to call in either case? @@ -203,16 +103,5 @@ func (r *ChainAgnosticBackFilledOracle) Close() error { // of its state. This will allow to re-start the process r.oracleStarted.Store(false) } - if r.cancelFn != nil { - // This is useful to step the previous tasks that are spawned in - // parallel before starting the Oracle. This will use the context to - // signal them to exit immediately. - // - // It can be possible this is the only way to stop the Start() async - // flow, specially when the previusly task are running (the replays) and - // `oracleStarted` would be false in that example. Calling `cancelFn()` - // will stop the replays and will prevent the oracle to start - r.cancelFn() - } return nil } diff --git a/core/services/ocr2/plugins/ccip/internal/oraclelib/backfilled_oracle_test.go b/core/services/ocr2/plugins/ccip/internal/oraclelib/backfilled_oracle_test.go deleted file mode 100644 index 6db1ebbadd9..00000000000 --- a/core/services/ocr2/plugins/ccip/internal/oraclelib/backfilled_oracle_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package oraclelib - -import ( - "testing" - - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" - "github.com/smartcontractkit/chainlink/v2/core/logger" - jobmocks "github.com/smartcontractkit/chainlink/v2/core/services/job/mocks" -) - -func TestBackfilledOracle(t *testing.T) { - // First scenario: Start() fails, check that all Replay are being called. - lp1 := lpmocks.NewLogPoller(t) - lp2 := lpmocks.NewLogPoller(t) - lp1.On("Replay", mock.Anything, int64(1)).Return(nil) - lp2.On("Replay", mock.Anything, int64(2)).Return(nil) - oracle1 := jobmocks.NewServiceCtx(t) - oracle1.On("Start", mock.Anything).Return(errors.New("Failed to start")).Twice() - job := NewBackfilledOracle(logger.TestLogger(t), lp1, lp2, 1, 2, oracle1) - - job.Run() - assert.False(t, job.IsRunning()) - job.Run() - assert.False(t, job.IsRunning()) - - /// Start -> Stop -> Start - oracle2 := jobmocks.NewServiceCtx(t) - oracle2.On("Start", mock.Anything).Return(nil).Twice() - oracle2.On("Close").Return(nil).Once() - - job2 := NewBackfilledOracle(logger.TestLogger(t), lp1, lp2, 1, 2, oracle2) - job2.Run() - assert.True(t, job2.IsRunning()) - assert.Nil(t, job2.Close()) - assert.False(t, job2.IsRunning()) - assert.Nil(t, job2.Close()) - assert.False(t, job2.IsRunning()) - job2.Run() - assert.True(t, job2.IsRunning()) - - /// Replay fails, but it starts anyway - lp11 := lpmocks.NewLogPoller(t) - lp12 := lpmocks.NewLogPoller(t) - lp11.On("Replay", mock.Anything, int64(1)).Return(errors.New("Replay failed")).Once() - lp12.On("Replay", mock.Anything, int64(2)).Return(errors.New("Replay failed")).Once() - - oracle := jobmocks.NewServiceCtx(t) - oracle.On("Start", mock.Anything).Return(nil).Once() - job3 := NewBackfilledOracle(logger.NullLogger, lp11, lp12, 1, 2, oracle) - job3.Run() - assert.True(t, job3.IsRunning()) -} diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline_test.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline_test.go index 8aeeff96b57..e71d5402503 100644 --- a/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline_test.go +++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline_test.go @@ -1,7 +1,6 @@ package pricegetter_test import ( - "context" "fmt" "math/big" "net/http" @@ -17,6 +16,7 @@ import ( config2 "github.com/smartcontractkit/chainlink-common/pkg/config" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -31,6 +31,7 @@ import ( ) func TestDataSource(t *testing.T) { + ctx := testutils.Context(t) linkEth := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, err := w.Write([]byte(`{"JuelsPerETH": "200000000000000000000"}`)) require.NoError(t, err) @@ -58,7 +59,7 @@ func TestDataSource(t *testing.T) { priceGetter := newTestPipelineGetter(t, source) // Ask for all prices present in spec. - prices, err := priceGetter.GetJobSpecTokenPricesUSD(context.Background()) + prices, err := priceGetter.GetJobSpecTokenPricesUSD(ctx) require.NoError(t, err) assert.Equal(t, prices, map[cciptypes.Address]*big.Int{ linkTokenAddress: big.NewInt(0).Mul(big.NewInt(200), big.NewInt(1000000000000000000)), @@ -66,7 +67,7 @@ func TestDataSource(t *testing.T) { }) // Specifically ask for all prices - pricesWithInput, errWithInput := priceGetter.TokenPricesUSD(context.Background(), []cciptypes.Address{ + pricesWithInput, errWithInput := priceGetter.TokenPricesUSD(ctx, []cciptypes.Address{ linkTokenAddress, usdcTokenAddress, }) @@ -77,13 +78,13 @@ func TestDataSource(t *testing.T) { }) // Ask a non-existent price. - _, err = priceGetter.TokenPricesUSD(context.Background(), []cciptypes.Address{ + _, err = priceGetter.TokenPricesUSD(ctx, []cciptypes.Address{ ccipcalc.HexToAddress("0x1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e11"), }) require.Error(t, err) // Ask only one price - prices, err = priceGetter.TokenPricesUSD(context.Background(), []cciptypes.Address{linkTokenAddress}) + prices, err = priceGetter.TokenPricesUSD(ctx, []cciptypes.Address{linkTokenAddress}) require.NoError(t, err) assert.Equal(t, prices, map[cciptypes.Address]*big.Int{ linkTokenAddress: big.NewInt(0).Mul(big.NewInt(200), big.NewInt(1000000000000000000)), @@ -135,6 +136,7 @@ func TestParsingDifferentFormats(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + ctx := testutils.Context(t) token := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, err := fmt.Fprintf(w, `{"MyCoin": %s}`, tt.inputValue) require.NoError(t, err) @@ -151,7 +153,7 @@ func TestParsingDifferentFormats(t *testing.T) { `, token.URL, strings.ToLower(address.String())) prices, err := newTestPipelineGetter(t, source). - TokenPricesUSD(context.Background(), []cciptypes.Address{ccipcalc.EvmAddrToGeneric(address)}) + TokenPricesUSD(ctx, []cciptypes.Address{ccipcalc.EvmAddrToGeneric(address)}) if tt.expectedError { require.Error(t, err) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go index 8410e6ff938..d69be750253 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/ccip_contracts.go @@ -22,6 +22,7 @@ import ( ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-common/pkg/hashutil" "github.com/smartcontractkit/chainlink-common/pkg/merklemulti" @@ -438,7 +439,7 @@ func (c *CCIPContracts) SetNopsOnRamp(t *testing.T, nopsAndWeights []evm_2_evm_o tx, err := c.Source.OnRamp.SetNops(c.Source.User, nopsAndWeights) require.NoError(t, err) c.Source.Chain.Commit() - _, err = bind.WaitMined(context.Background(), c.Source.Chain, tx) + _, err = bind.WaitMined(tests.Context(t), c.Source.Chain, tx) require.NoError(t, err) } @@ -578,7 +579,7 @@ func (c *CCIPContracts) SetupExecOCR2Config(t *testing.T, execOnchainConfig, exe func (c *CCIPContracts) SetupOnchainConfig(t *testing.T, commitOnchainConfig, commitOffchainConfig, execOnchainConfig, execOffchainConfig []byte) int64 { // Note We do NOT set the payees, payment is done in the OCR2Base implementation - blockBeforeConfig, err := c.Dest.Chain.BlockByNumber(context.Background(), nil) + blockBeforeConfig, err := c.Dest.Chain.BlockByNumber(tests.Context(t), nil) require.NoError(t, err) c.SetupCommitOCR2Config(t, commitOnchainConfig, commitOffchainConfig) @@ -1292,8 +1293,8 @@ type ManualExecArgs struct { // if the block located has a timestamp greater than the timestamp of mentioned source block // it just returns the first block found with lesser timestamp of the source block // providing a value of args.DestDeployedAt ensures better performance by reducing the range of block numbers to be traversed -func (args *ManualExecArgs) ApproxDestStartBlock() error { - sourceBlockHdr, err := args.SourceChain.HeaderByNumber(context.Background(), args.SourceStartBlock) +func (args *ManualExecArgs) ApproxDestStartBlock(ctx context.Context) error { + sourceBlockHdr, err := args.SourceChain.HeaderByNumber(ctx, args.SourceStartBlock) if err != nil { return err } @@ -1303,7 +1304,7 @@ func (args *ManualExecArgs) ApproxDestStartBlock() error { minBlockNum := args.DestDeployedAt closestBlockNum := uint64(math.Floor((float64(maxBlockNum) + float64(minBlockNum)) / 2)) var closestBlockHdr *types.Header - closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum))) + closestBlockHdr, err = args.DestChain.HeaderByNumber(ctx, new(big.Int).SetUint64(closestBlockNum)) if err != nil { return err } @@ -1324,7 +1325,7 @@ func (args *ManualExecArgs) ApproxDestStartBlock() error { minBlockNum = blockNum + 1 } closestBlockNum = uint64(math.Floor((float64(maxBlockNum) + float64(minBlockNum)) / 2)) - closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum))) + closestBlockHdr, err = args.DestChain.HeaderByNumber(ctx, new(big.Int).SetUint64(closestBlockNum)) if err != nil { return err } @@ -1335,7 +1336,7 @@ func (args *ManualExecArgs) ApproxDestStartBlock() error { if closestBlockNum <= 0 { return fmt.Errorf("approx destination blocknumber not found") } - closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum))) + closestBlockHdr, err = args.DestChain.HeaderByNumber(ctx, new(big.Int).SetUint64(closestBlockNum)) if err != nil { return err } @@ -1371,7 +1372,7 @@ func (args *ManualExecArgs) FindSeqNrFromCCIPSendRequested() (uint64, error) { return seqNr, nil } -func (args *ManualExecArgs) ExecuteManually() (*types.Transaction, error) { +func (args *ManualExecArgs) ExecuteManually(ctx context.Context) (*types.Transaction, error) { if args.SourceChainID == 0 || args.DestChainID == 0 || args.DestUser == nil { @@ -1404,7 +1405,7 @@ func (args *ManualExecArgs) ExecuteManually() (*types.Transaction, error) { return nil, err } if args.DestStartBlock < 1 { - err = args.ApproxDestStartBlock() + err = args.ApproxDestStartBlock(ctx) if err != nil { return nil, err } @@ -1571,7 +1572,7 @@ func (c *CCIPContracts) ExecuteMessage( destStartBlock uint64, ) uint64 { t.Log("Executing request manually") - sendReqReceipt, err := c.Source.Chain.TransactionReceipt(context.Background(), txHash) + sendReqReceipt, err := c.Source.Chain.TransactionReceipt(tests.Context(t), txHash) require.NoError(t, err) args := ManualExecArgs{ SourceChainID: c.Source.ChainID, @@ -1588,11 +1589,12 @@ func (c *CCIPContracts) ExecuteMessage( OnRamp: c.Source.OnRamp.Address().String(), OffRamp: c.Dest.OffRamp.Address().String(), } - tx, err := args.ExecuteManually() + ctx := tests.Context(t) + tx, err := args.ExecuteManually(ctx) require.NoError(t, err) c.Dest.Chain.Commit() c.Source.Chain.Commit() - rec, err := c.Dest.Chain.TransactionReceipt(context.Background(), tx.Hash()) + rec, err := c.Dest.Chain.TransactionReceipt(ctx, tx.Hash()) require.NoError(t, err) require.Equal(t, uint64(1), rec.Status, "manual execution failed") t.Logf("Manual Execution completed for seqNum %d", args.SeqNr) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go index d0d502e8673..0b7f0de4d25 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/integration/chainlink.go @@ -35,6 +35,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks" @@ -342,7 +343,7 @@ func (node *Node) AddJob(t *testing.T, spec *OCR2TaskJobSpec) { nil, ) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &ccipJob) + err = node.App.AddJobV2(tests.Context(t), &ccipJob) require.NoError(t, err) } @@ -351,7 +352,7 @@ func (node *Node) AddBootstrapJob(t *testing.T, spec *OCR2TaskJobSpec) { require.NoError(t, err) ccipJob, err := ocrbootstrap.ValidatedBootstrapSpecToml(specString) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &ccipJob) + err = node.App.AddJobV2(tests.Context(t), &ccipJob) require.NoError(t, err) } @@ -512,13 +513,13 @@ func setupNodeCCIP( lggr.Debug(fmt.Sprintf("Transmitter address %s chainID %s", transmitter, s.EVMChainID.String())) // Fund the commitTransmitter address with some ETH - n, err := destChain.NonceAt(context.Background(), owner.From, nil) + n, err := destChain.NonceAt(tests.Context(t), owner.From, nil) require.NoError(t, err) tx := types3.NewTransaction(n, transmitter, big.NewInt(1000000000000000000), 21000, big.NewInt(1000000000), nil) signedTx, err := owner.Signer(owner.From, tx) require.NoError(t, err) - err = destChain.SendTransaction(context.Background(), signedTx) + err = destChain.SendTransaction(tests.Context(t), signedTx) require.NoError(t, err) destChain.Commit() @@ -998,7 +999,7 @@ func (c *CCIPIntegrationTestHarness) SetupAndStartNodes(ctx context.Context, t * func (c *CCIPIntegrationTestHarness) SetUpNodesAndJobs(t *testing.T, pricePipeline string, priceGetterConfig string, usdcAttestationAPI string) CCIPJobSpecParams { // setup Jobs - ctx := context.Background() + ctx := tests.Context(t) // Starts nodes and configures them in the OCR contracts. bootstrapNode, _, configBlock := c.SetupAndStartNodes(ctx, t, int64(freeport.GetOne(t))) @@ -1011,7 +1012,7 @@ func (c *CCIPIntegrationTestHarness) SetUpNodesAndJobs(t *testing.T, pricePipeli // Replay for bootstrap. bc, err := bootstrapNode.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(c.Dest.ChainID, 10)) require.NoError(t, err) - require.NoError(t, bc.LogPoller().Replay(context.Background(), configBlock)) + require.NoError(t, bc.LogPoller().Replay(ctx, configBlock)) c.Dest.Chain.Commit() return jobParams diff --git a/core/services/ocr2/plugins/ccip/testhelpers/simulated_backend.go b/core/services/ocr2/plugins/ccip/testhelpers/simulated_backend.go index ea91362aaae..f48027545ad 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/simulated_backend.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/simulated_backend.go @@ -1,20 +1,19 @@ package testhelpers import ( - "context" "math/big" "testing" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ) @@ -54,21 +53,13 @@ func (ks EthKeyStoreSim) Eth() keystore.Eth { return ks.ETHKS } -func (ks EthKeyStoreSim) SignTx(address common.Address, tx *ethtypes.Transaction, chainID *big.Int) (*ethtypes.Transaction, error) { - if chainID.String() == "1000" { - // A terrible hack, just for the multichain test. All simulation clients run on chainID 1337. - // We let the DestChainSelector actually use 1337 to make sure the offchainConfig digests are properly generated. - return ks.ETHKS.SignTx(context.Background(), address, tx, big.NewInt(1337)) - } - return ks.ETHKS.SignTx(context.Background(), address, tx, chainID) -} - var _ keystore.Eth = EthKeyStoreSim{}.ETHKS func ConfirmTxs(t *testing.T, txs []*ethtypes.Transaction, chain *backends.SimulatedBackend) { chain.Commit() + ctx := tests.Context(t) for _, tx := range txs { - rec, err := bind.WaitMined(context.Background(), chain, tx) + rec, err := bind.WaitMined(ctx, chain, tx) require.NoError(t, err) require.Equal(t, uint64(1), rec.Status) } diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go index 64d3b5d26c1..ccdc93660c2 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/ccip_contracts_1_4_0.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/hashutil" "github.com/smartcontractkit/chainlink-common/pkg/merklemulti" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -443,7 +444,7 @@ func (c *CCIPContracts) SetNopsOnRamp(t *testing.T, nopsAndWeights []evm_2_evm_o tx, err := c.Source.OnRamp.SetNops(c.Source.User, nopsAndWeights) require.NoError(t, err) c.Source.Chain.Commit() - _, err = bind.WaitMined(context.Background(), c.Source.Chain, tx) + _, err = bind.WaitMined(tests.Context(t), c.Source.Chain, tx) require.NoError(t, err) } @@ -583,7 +584,7 @@ func (c *CCIPContracts) SetupExecOCR2Config(t *testing.T, execOnchainConfig, exe func (c *CCIPContracts) SetupOnchainConfig(t *testing.T, commitOnchainConfig, commitOffchainConfig, execOnchainConfig, execOffchainConfig []byte) int64 { // Note We do NOT set the payees, payment is done in the OCR2Base implementation - blockBeforeConfig, err := c.Dest.Chain.BlockByNumber(context.Background(), nil) + blockBeforeConfig, err := c.Dest.Chain.BlockByNumber(tests.Context(t), nil) require.NoError(t, err) c.SetupCommitOCR2Config(t, commitOnchainConfig, commitOffchainConfig) @@ -1304,8 +1305,8 @@ type ManualExecArgs struct { // if the block located has a timestamp greater than the timestamp of mentioned source block // it just returns the first block found with lesser timestamp of the source block // providing a value of args.DestDeployedAt ensures better performance by reducing the range of block numbers to be traversed -func (args *ManualExecArgs) ApproxDestStartBlock() error { - sourceBlockHdr, err := args.SourceChain.HeaderByNumber(context.Background(), args.SourceStartBlock) +func (args *ManualExecArgs) ApproxDestStartBlock(ctx context.Context) error { + sourceBlockHdr, err := args.SourceChain.HeaderByNumber(ctx, args.SourceStartBlock) if err != nil { return err } @@ -1315,7 +1316,7 @@ func (args *ManualExecArgs) ApproxDestStartBlock() error { minBlockNum := args.DestDeployedAt closestBlockNum := uint64(math.Floor((float64(maxBlockNum) + float64(minBlockNum)) / 2)) var closestBlockHdr *types.Header - closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum))) + closestBlockHdr, err = args.DestChain.HeaderByNumber(ctx, new(big.Int).SetUint64(closestBlockNum)) if err != nil { return err } @@ -1336,7 +1337,7 @@ func (args *ManualExecArgs) ApproxDestStartBlock() error { minBlockNum = blockNum + 1 } closestBlockNum = uint64(math.Floor((float64(maxBlockNum) + float64(minBlockNum)) / 2)) - closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum))) + closestBlockHdr, err = args.DestChain.HeaderByNumber(ctx, new(big.Int).SetUint64(closestBlockNum)) if err != nil { return err } @@ -1347,7 +1348,7 @@ func (args *ManualExecArgs) ApproxDestStartBlock() error { if closestBlockNum <= 0 { return fmt.Errorf("approx destination blocknumber not found") } - closestBlockHdr, err = args.DestChain.HeaderByNumber(context.Background(), big.NewInt(int64(closestBlockNum))) + closestBlockHdr, err = args.DestChain.HeaderByNumber(ctx, new(big.Int).SetUint64(closestBlockNum)) if err != nil { return err } @@ -1383,7 +1384,7 @@ func (args *ManualExecArgs) FindSeqNrFromCCIPSendRequested() (uint64, error) { return seqNr, nil } -func (args *ManualExecArgs) ExecuteManually() (*types.Transaction, error) { +func (args *ManualExecArgs) ExecuteManually(ctx context.Context) (*types.Transaction, error) { if args.SourceChainID == 0 || args.DestChainID == 0 || args.DestUser == nil { @@ -1416,7 +1417,7 @@ func (args *ManualExecArgs) ExecuteManually() (*types.Transaction, error) { return nil, err } if args.DestStartBlock < 1 { - err = args.ApproxDestStartBlock() + err = args.ApproxDestStartBlock(ctx) if err != nil { return nil, err } @@ -1553,7 +1554,8 @@ func (c *CCIPContracts) ExecuteMessage( destStartBlock uint64, ) uint64 { t.Log("Executing request manually") - sendReqReceipt, err := c.Source.Chain.TransactionReceipt(context.Background(), txHash) + ctx := tests.Context(t) + sendReqReceipt, err := c.Source.Chain.TransactionReceipt(ctx, txHash) require.NoError(t, err) args := ManualExecArgs{ SourceChainID: c.Source.ChainID, @@ -1570,11 +1572,11 @@ func (c *CCIPContracts) ExecuteMessage( OnRamp: c.Source.OnRamp.Address().String(), OffRamp: c.Dest.OffRamp.Address().String(), } - tx, err := args.ExecuteManually() + tx, err := args.ExecuteManually(ctx) require.NoError(t, err) c.Dest.Chain.Commit() c.Source.Chain.Commit() - rec, err := c.Dest.Chain.TransactionReceipt(context.Background(), tx.Hash()) + rec, err := c.Dest.Chain.TransactionReceipt(tests.Context(t), tx.Hash()) require.NoError(t, err) require.Equal(t, uint64(1), rec.Status, "manual execution failed") t.Logf("Manual Execution completed for seqNum %d", args.SeqNr) diff --git a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go index a69e284e548..b897d565bae 100644 --- a/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go +++ b/core/services/ocr2/plugins/ccip/testhelpers/testhelpers_1_4_0/chainlink.go @@ -34,6 +34,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core/mocks" @@ -339,7 +340,7 @@ func (node *Node) AddJob(t *testing.T, spec *integrationtesthelpers.OCR2TaskJobS nil, ) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &ccipJob) + err = node.App.AddJobV2(tests.Context(t), &ccipJob) require.NoError(t, err) } @@ -348,7 +349,7 @@ func (node *Node) AddBootstrapJob(t *testing.T, spec *integrationtesthelpers.OCR require.NoError(t, err) ccipJob, err := ocrbootstrap.ValidatedBootstrapSpecToml(specString) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &ccipJob) + err = node.App.AddJobV2(tests.Context(t), &ccipJob) require.NoError(t, err) } @@ -508,13 +509,13 @@ func setupNodeCCIP( lggr.Debug(fmt.Sprintf("Transmitter address %s chainID %s", transmitter, s.EVMChainID.String())) // Fund the commitTransmitter address with some ETH - n, err := destChain.NonceAt(context.Background(), owner.From, nil) + n, err := destChain.NonceAt(tests.Context(t), owner.From, nil) require.NoError(t, err) tx := types3.NewTransaction(n, transmitter, big.NewInt(1000000000000000000), 21000, big.NewInt(1000000000), nil) signedTx, err := owner.Signer(owner.From, tx) require.NoError(t, err) - err = destChain.SendTransaction(context.Background(), signedTx) + err = destChain.SendTransaction(tests.Context(t), signedTx) require.NoError(t, err) destChain.Commit() @@ -944,7 +945,7 @@ func (c *CCIPIntegrationTestHarness) SetupAndStartNodes(ctx context.Context, t * func (c *CCIPIntegrationTestHarness) SetUpNodesAndJobs(t *testing.T, pricePipeline string, priceGetterConfig string, usdcAttestationAPI string) integrationtesthelpers.CCIPJobSpecParams { // setup Jobs - ctx := context.Background() + ctx := tests.Context(t) // Starts nodes and configures them in the OCR contracts. bootstrapNode, _, configBlock := c.SetupAndStartNodes(ctx, t, int64(freeport.GetOne(t))) @@ -957,7 +958,7 @@ func (c *CCIPIntegrationTestHarness) SetUpNodesAndJobs(t *testing.T, pricePipeli // Replay for bootstrap. bc, err := bootstrapNode.App.GetRelayers().LegacyEVMChains().Get(strconv.FormatUint(c.Dest.ChainID, 10)) require.NoError(t, err) - require.NoError(t, bc.LogPoller().Replay(context.Background(), configBlock)) + require.NoError(t, bc.LogPoller().Replay(tests.Context(t), configBlock)) c.Dest.Chain.Commit() return jobParams diff --git a/core/services/ocr2/plugins/ccip/tokendata/bgworker.go b/core/services/ocr2/plugins/ccip/tokendata/bgworker.go index 1a74ab2305b..bc5aba557e6 100644 --- a/core/services/ocr2/plugins/ccip/tokendata/bgworker.go +++ b/core/services/ocr2/plugins/ccip/tokendata/bgworker.go @@ -41,9 +41,8 @@ type BackgroundWorker struct { timeoutDur time.Duration services.StateMachine - wg *sync.WaitGroup - backgroundCtx context.Context //nolint:containedctx - backgroundCancel context.CancelFunc + wg sync.WaitGroup + stopChan services.StopChan } func NewBackgroundWorker( @@ -56,17 +55,13 @@ func NewBackgroundWorker( expirationDur = 24 * time.Hour } - ctx, cancel := context.WithCancel(context.Background()) return &BackgroundWorker{ tokenDataReaders: tokenDataReaders, numWorkers: numWorkers, jobsChan: make(chan cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, numWorkers*100), resultsCache: cache.New(expirationDur, expirationDur/2), timeoutDur: timeoutDur, - - wg: new(sync.WaitGroup), - backgroundCtx: ctx, - backgroundCancel: cancel, + stopChan: make(services.StopChan), } } @@ -82,7 +77,7 @@ func (w *BackgroundWorker) Start(context.Context) error { func (w *BackgroundWorker) Close() error { return w.StateMachine.StopOnce("Token BackgroundWorker", func() error { - w.backgroundCancel() + close(w.stopChan) w.wg.Wait() return nil }) @@ -90,12 +85,13 @@ func (w *BackgroundWorker) Close() error { func (w *BackgroundWorker) AddJobsFromMsgs(ctx context.Context, msgs []cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta) { w.wg.Add(1) - go func() { + go func(ctx context.Context) { defer w.wg.Done() + ctx, cancel := w.stopChan.Ctx(ctx) + defer cancel() + for _, msg := range msgs { select { - case <-w.backgroundCtx.Done(): - return case <-ctx.Done(): return default: @@ -104,7 +100,7 @@ func (w *BackgroundWorker) AddJobsFromMsgs(ctx context.Context, msgs []cciptypes } } } - }() + }(ctx) } func (w *BackgroundWorker) GetReaders() map[cciptypes.Address]Reader { @@ -134,12 +130,15 @@ func (w *BackgroundWorker) GetMsgTokenData(ctx context.Context, msg cciptypes.EV func (w *BackgroundWorker) run() { go func() { defer w.wg.Done() + ctx, cancel := w.stopChan.NewCtx() + defer cancel() + for { select { - case <-w.backgroundCtx.Done(): + case <-ctx.Done(): return case msg := <-w.jobsChan: - w.workOnMsg(w.backgroundCtx, msg) + w.workOnMsg(ctx, msg) } } }() diff --git a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go index 4210ecf75ea..786e88a6322 100644 --- a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go +++ b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc_test.go @@ -20,9 +20,11 @@ import ( "github.com/stretchr/testify/require" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" @@ -35,15 +37,17 @@ var ( ) func TestUSDCReader_callAttestationApi(t *testing.T) { + ctx := tests.Context(t) //nolint:staticcheck // SA4006 - false positive "unused" t.Skipf("Skipping test because it uses the real USDC attestation API") usdcMessageHash := "912f22a13e9ccb979b621500f6952b2afd6e75be7eadaed93fc2625fe11c52a2" attestationURI, err := url.ParseRequestURI("https://iris-api-sandbox.circle.com") require.NoError(t, err) lggr := logger.TestLogger(t) - usdcReader, _ := ccipdata.NewUSDCReader(lggr, "job_123", mockMsgTransmitter, nil, false) + usdcReader, err := ccipdata.NewUSDCReader(ctx, lggr, "job_123", mockMsgTransmitter, nil, false) + require.NoError(t, err) usdcService := NewUSDCTokenDataReader(lggr, usdcReader, attestationURI, 0, common.Address{}, APIIntervalRateLimitDisabled) - attestation, err := usdcService.callAttestationApi(context.Background(), [32]byte(common.FromHex(usdcMessageHash))) + attestation, err := usdcService.callAttestationApi(ctx, [32]byte(common.FromHex(usdcMessageHash))) require.NoError(t, err) require.Equal(t, attestationStatusPending, attestation.Status) @@ -52,6 +56,7 @@ func TestUSDCReader_callAttestationApi(t *testing.T) { func TestUSDCReader_callAttestationApiMock(t *testing.T) { t.Parallel() + ctx := tests.Context(t) response := attestationResponse{ Status: attestationStatusSuccess, Attestation: "720502893578a89a8a87982982ef781c18b193", @@ -64,9 +69,9 @@ func TestUSDCReader_callAttestationApiMock(t *testing.T) { lggr := logger.TestLogger(t) lp := mocks.NewLogPoller(t) - usdcReader, _ := ccipdata.NewUSDCReader(lggr, "job_123", mockMsgTransmitter, lp, false) + usdcReader, _ := ccipdata.NewUSDCReader(ctx, lggr, "job_123", mockMsgTransmitter, lp, false) usdcService := NewUSDCTokenDataReader(lggr, usdcReader, attestationURI, 0, common.Address{}, APIIntervalRateLimitDisabled) - attestation, err := usdcService.callAttestationApi(context.Background(), utils.RandomBytes32()) + attestation, err := usdcService.callAttestationApi(ctx, utils.RandomBytes32()) require.NoError(t, err) require.Equal(t, response.Status, attestation.Status) @@ -196,12 +201,13 @@ func TestUSDCReader_callAttestationApiMockError(t *testing.T) { lggr := logger.TestLogger(t) lp := mocks.NewLogPoller(t) - usdcReader, _ := ccipdata.NewUSDCReader(lggr, "job_123", mockMsgTransmitter, lp, false) + ctx := testutils.Context(t) + usdcReader, _ := ccipdata.NewUSDCReader(ctx, lggr, "job_123", mockMsgTransmitter, lp, false) usdcService := NewUSDCTokenDataReader(lggr, usdcReader, attestationURI, test.customTimeoutSeconds, common.Address{}, APIIntervalRateLimitDisabled) lp.On("RegisterFilter", mock.Anything, mock.Anything).Return(nil) - require.NoError(t, usdcReader.RegisterFilters()) + require.NoError(t, usdcReader.RegisterFilters(ctx)) - parentCtx, cancel := context.WithTimeout(context.Background(), time.Duration(test.parentTimeoutSeconds)*time.Second) + parentCtx, cancel := context.WithTimeout(ctx, time.Duration(test.parentTimeoutSeconds)*time.Second) defer cancel() _, err = usdcService.callAttestationApi(parentCtx, utils.RandomBytes32()) @@ -228,6 +234,7 @@ func getMockUSDCEndpoint(t *testing.T, response attestationResponse) *httptest.S func TestGetUSDCMessageBody(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) expectedBody := []byte("0x0000000000000001000000020000000000048d71000000000000000000000000eb08f243e5d3fcff26a9e38ae5520a669f4019d000000000000000000000000023a04d5935ed8bc8e3eb78db3541f0abfb001c6e0000000000000000000000006cb3ed9b441eb674b58495c8b3324b59faff5243000000000000000000000000000000005425890298aed601595a70ab815c96711a31bc65000000000000000000000000ab4f961939bfe6a93567cc57c59eed7084ce2131000000000000000000000000000000000000000000000000000000000000271000000000000000000000000035e08285cfed1ef159236728f843286c55fc0861") usdcReader := ccipdatamocks.USDCReader{} usdcReader.On("GetUSDCMessagePriorToLogIndexInTx", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(expectedBody, nil) @@ -237,7 +244,7 @@ func TestGetUSDCMessageBody(t *testing.T) { usdcService := NewUSDCTokenDataReader(lggr, &usdcReader, nil, 0, usdcTokenAddr, APIIntervalRateLimitDisabled) // Make the first call and assert the underlying function is called - body, err := usdcService.getUSDCMessageBody(context.Background(), cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta{ + body, err := usdcService.getUSDCMessageBody(ctx, cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta{ EVM2EVMMessage: cciptypes.EVM2EVMMessage{ TokenAmounts: []cciptypes.TokenAmount{ { @@ -356,6 +363,7 @@ func TestUSDCReader_rateLimiting(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() + ctx := tests.Context(t) response := attestationResponse{ Status: attestationStatusSuccess, @@ -369,10 +377,9 @@ func TestUSDCReader_rateLimiting(t *testing.T) { lggr := logger.TestLogger(t) lp := mocks.NewLogPoller(t) - usdcReader, _ := ccipdata.NewUSDCReader(lggr, "job_123", mockMsgTransmitter, lp, false) + usdcReader, _ := ccipdata.NewUSDCReader(ctx, lggr, "job_123", mockMsgTransmitter, lp, false) usdcService := NewUSDCTokenDataReader(lggr, usdcReader, attestationURI, 0, utils.RandomAddress(), tc.rateConfig) - ctx := context.Background() if tc.timeout > 0 { var cf context.CancelFunc ctx, cf = context.WithTimeout(ctx, tc.timeout) diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go index 7ab735bf122..206f8012e8b 100644 --- a/core/services/ocr2/plugins/llo/integration_test.go +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -323,6 +323,7 @@ func promoteStagingConfig(t *testing.T, donID uint32, steve *bind.TransactOpts, } func TestIntegration_LLO(t *testing.T) { + t.Parallel() testStartTimeStamp := time.Now() multiplier := decimal.New(1, 18) expirationWindow := time.Hour / time.Second @@ -808,7 +809,8 @@ func setupNodes(t *testing.T, nNodes int, backend *backends.SimulatedBackend, cl nodes = append(nodes, Node{ app, transmitter, kb, observedLogs, }) - offchainPublicKey, _ := hex.DecodeString(strings.TrimPrefix(kb.OnChainPublicKey(), "0x")) + offchainPublicKey, err := hex.DecodeString(strings.TrimPrefix(kb.OnChainPublicKey(), "0x")) + require.NoError(t, err) oracles = append(oracles, confighelper.OracleIdentityExtra{ OracleIdentity: confighelper.OracleIdentity{ OnchainPublicKey: offchainPublicKey, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go index c02b7c10de5..4adef132aab 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go @@ -70,8 +70,7 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo }) } - // TODO (AUTO 9090): Understand and fix the use of context.Background() here - reqTimeoutCtx, cancel := context.WithTimeout(context.Background(), mercury.RequestTimeout) + ctx, cancel := context.WithTimeout(ctx, mercury.RequestTimeout) defer cancel() state := encoding.NoPipelineError @@ -86,7 +85,7 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo // if no execution errors, then check if any feed returned an error code, if so use the last error code for i := 0; i < resultLen; i++ { select { - case <-reqTimeoutCtx.Done(): + case <-ctx.Done(): // Request Timed out, return timeout error c.lggr.Errorf("at block %s upkeep %s, streams lookup v0.2 timed out", streamsLookup.Time.String(), streamsLookup.UpkeepId.String()) return encoding.NoPipelineError, nil, encoding.ErrCodeStreamsTimeout, false, 0 * time.Second, nil diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go index 3ade8cc7261..16892c88a59 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go @@ -74,11 +74,10 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo c.multiFeedsRequest(ctx, ch, streamsLookup) }) - // TODO (AUTO 9090): Understand and fix the use of context.Background() here - reqTimeoutCtx, cancel := context.WithTimeout(context.Background(), mercury.RequestTimeout) + ctx, cancel := context.WithTimeout(ctx, mercury.RequestTimeout) defer cancel() select { - case <-reqTimeoutCtx.Done(): + case <-ctx.Done(): // Request Timed out, return timeout error c.lggr.Errorf("at timestamp %s upkeep %s, streams lookup v0.3 timed out", streamsLookup.Time.String(), streamsLookup.UpkeepId.String()) return encoding.NoPipelineError, nil, encoding.ErrCodeStreamsTimeout, false, 0 * time.Second, nil diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 185504fc0e4..1fc2fc46336 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -384,7 +384,7 @@ func (r *runner) run(ctx context.Context, pipeline *Pipeline, run *Run, vars Var // This is "just in case" for cleaning up any stray reports. // Normally the scheduler loop doesn't stop until all in progress runs report back - reportCtx, cancel := context.WithCancel(context.Background()) + reportCtx, cancel := context.WithCancel(context.WithoutCancel(ctx)) defer cancel() if pipelineTimeout := r.config.MaxRunDuration(); pipelineTimeout != 0 { diff --git a/core/services/registrysyncer/monitoring.go b/core/services/registrysyncer/monitoring.go index db374b523ff..97fd181515c 100644 --- a/core/services/registrysyncer/monitoring.go +++ b/core/services/registrysyncer/monitoring.go @@ -16,12 +16,12 @@ var remoteRegistrySyncFailureCounter metric.Int64Counter var launcherFailureCounter metric.Int64Counter func initMonitoringResources() (err error) { - remoteRegistrySyncFailureCounter, err = beholder.GetMeter().Int64Counter("platform.registry_syncer.sync.failures") + remoteRegistrySyncFailureCounter, err = beholder.GetMeter().Int64Counter("platform_registrysyncer_sync_failures") if err != nil { return fmt.Errorf("failed to register sync failure counter: %w", err) } - launcherFailureCounter, err = beholder.GetMeter().Int64Counter("platform.registry_syncer.launch.failures") + launcherFailureCounter, err = beholder.GetMeter().Int64Counter("platform_registrysyncer_launch_failures") if err != nil { return fmt.Errorf("failed to register launcher failure counter: %w", err) } diff --git a/core/services/relay/evm/ccip.go b/core/services/relay/evm/ccip.go index 3eefb7bec7b..a06f60c6fd4 100644 --- a/core/services/relay/evm/ccip.go +++ b/core/services/relay/evm/ccip.go @@ -131,8 +131,8 @@ type IncompleteDestCommitStoreReader struct { cs cciptypes.CommitStoreReader } -func NewIncompleteDestCommitStoreReader(lggr logger.Logger, versionFinder ccip.VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) (*IncompleteDestCommitStoreReader, error) { - cs, err := ccip.NewCommitStoreReader(lggr, versionFinder, address, ec, lp) +func NewIncompleteDestCommitStoreReader(ctx context.Context, lggr logger.Logger, versionFinder ccip.VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller) (*IncompleteDestCommitStoreReader, error) { + cs, err := ccip.NewCommitStoreReader(ctx, lggr, versionFinder, address, ec, lp) if err != nil { return nil, err } diff --git a/core/services/relay/evm/chain_reader.go b/core/services/relay/evm/chain_reader.go index c734ade1104..df216a11c2b 100644 --- a/core/services/relay/evm/chain_reader.go +++ b/core/services/relay/evm/chain_reader.go @@ -302,7 +302,7 @@ func (cr *chainReader) addEvent(contractName, eventName string, a abi.ABI, chain return fmt.Errorf("%w: event %q doesn't exist", commontypes.ErrInvalidConfig, chainReaderDefinition.ChainSpecificName) } - indexedAsUnIndexedABITypes, indexedTopicsCodecTypes, eventDWs := getEventTypes(event) + indexedAsUnIndexedABITypes, indexedTopicsCodecTypes, dwsDetails := getEventTypes(event) if err := indexedTopicsCodecTypes.Init(); err != nil { return err } @@ -337,7 +337,7 @@ func (cr *chainReader) addEvent(contractName, eventName string, a abi.ABI, chain maps.Copy(codecModifiers, topicsModifiers) // TODO BCFR-44 no dw modifier for now - dataWordsDetails, dWSCodecTypeInfo, initDWQueryingErr := cr.initDWQuerying(contractName, eventName, eventDWs, eventDefinitions.GenericDataWordDetails) + dataWordsDetails, dWSCodecTypeInfo, initDWQueryingErr := cr.initDWQuerying(contractName, eventName, dwsDetails, eventDefinitions.GenericDataWordDetails) if initDWQueryingErr != nil { return fmt.Errorf("failed to init dw querying for event: %q, err: %w", eventName, initDWQueryingErr) } @@ -473,43 +473,61 @@ func (cr *chainReader) addDecoderDef(contractName, itemType string, outputs abi. func getEventTypes(event abi.Event) ([]abi.Argument, types.CodecEntry, map[string]read.DataWordDetail) { indexedAsUnIndexedTypes := make([]abi.Argument, 0, types.MaxTopicFields) indexedTypes := make([]abi.Argument, 0, len(event.Inputs)) - dataWords := make(map[string]read.DataWordDetail) - var dwIndex int - for _, input := range event.Inputs { - if !input.Indexed { - dwIndex = calculateFieldDWIndex(input, event.Name+"."+input.Name, dataWords, dwIndex) - continue + if input.Indexed { + indexedAsUnIndexed := input + indexedAsUnIndexed.Indexed = false + // when presenting the filter off-chain, the caller will provide the unHashed version of the input and CR will hash topics when needed. + indexedAsUnIndexedTypes = append(indexedAsUnIndexedTypes, indexedAsUnIndexed) + indexedTypes = append(indexedTypes, input) } - - indexedAsUnIndexed := input - indexedAsUnIndexed.Indexed = false - // when presenting the filter off-chain, the caller will provide the unHashed version of the input and CR will hash topics when needed. - indexedAsUnIndexedTypes = append(indexedAsUnIndexedTypes, indexedAsUnIndexed) - indexedTypes = append(indexedTypes, input) } - return indexedAsUnIndexedTypes, types.NewCodecEntry(indexedTypes, nil, nil), dataWords + return indexedAsUnIndexedTypes, types.NewCodecEntry(indexedTypes, nil, nil), getDWIndexesWithTypes(event.Name, event.Inputs) } -// calculateFieldDWIndex recursively calculates the indices of all static unindexed fields in the event -// and calculates the offset for all unsearchable / dynamic fields. -func calculateFieldDWIndex(arg abi.Argument, fieldPath string, dataWords map[string]read.DataWordDetail, index int) int { - if isDynamic(arg.Type) { - return index + 1 +func getDWIndexesWithTypes(eventName string, eventInputs abi.Arguments) map[string]read.DataWordDetail { + var dwIndexOffset int + dataWords := make(map[string]read.DataWordDetail) + dynamicQueue := make([]abi.Argument, 0) + + for _, input := range eventInputs.NonIndexed() { + // each dynamic field has an extra field that stores the dwIndexOffset that points to the start of the dynamic data. + if isDynamic(input.Type) { + dynamicQueue = append(dynamicQueue, input) + dwIndexOffset++ + } else { + dwIndexOffset = processDWStaticField(input.Type, eventName+"."+input.Name, dataWords, dwIndexOffset) + } } - return processFields(arg.Type, fieldPath, dataWords, index) + processDWDynamicFields(eventName, dataWords, dynamicQueue, dwIndexOffset) + return dataWords +} + +func isDynamic(fieldType abi.Type) bool { + switch fieldType.T { + case abi.StringTy, abi.SliceTy, abi.BytesTy: + return true + case abi.TupleTy: + // If one element in a struct is dynamic, the whole struct is treated as dynamic. + for _, elem := range fieldType.TupleElems { + if isDynamic(*elem) { + return true + } + } + } + return false } -func processFields(fieldType abi.Type, parentFieldPath string, dataWords map[string]read.DataWordDetail, index int) int { +func processDWStaticField(fieldType abi.Type, parentFieldPath string, dataWords map[string]read.DataWordDetail, index int) int { switch fieldType.T { case abi.TupleTy: // Recursively process tuple elements for i, tupleElem := range fieldType.TupleElems { fieldName := fieldType.TupleRawNames[i] fullFieldPath := fmt.Sprintf("%s.%s", parentFieldPath, fieldName) - index = processFields(*tupleElem, fullFieldPath, dataWords, index) + index = processDWStaticField(*tupleElem, fullFieldPath, dataWords, index) } return index case abi.ArrayTy: @@ -525,19 +543,24 @@ func processFields(fieldType abi.Type, parentFieldPath string, dataWords map[str } } -func isDynamic(fieldType abi.Type) bool { - switch fieldType.T { - case abi.StringTy, abi.SliceTy, abi.BytesTy: - return true - case abi.TupleTy: - // If one element in a struct is dynamic, the whole struct is treated as dynamic. - for _, elem := range fieldType.TupleElems { - if isDynamic(*elem) { - return true +// processDWDynamicFields indexes static fields in dynamic structs. +// These fields come first after the static fields in the event encoding, so we can calculate their indices. +func processDWDynamicFields(eventName string, dataWords map[string]read.DataWordDetail, dynamicQueue []abi.Argument, dwIndex int) { + for _, arg := range dynamicQueue { + switch arg.Type.T { + case abi.TupleTy: + for i, tupleElem := range arg.Type.TupleElems { + // any field after a dynamic field can't be predictably indexed. + if isDynamic(*tupleElem) { + return + } + dwIndex = processDWStaticField(*tupleElem, fmt.Sprintf("%s.%s.%s", eventName, arg.Name, arg.Type.TupleRawNames[i]), dataWords, dwIndex) } + default: + // exit if we see a dynamic field, as we can't predict the index of fields after it. + return } } - return false } // ConfirmationsFromConfig maps chain agnostic confidence levels defined in config to predefined EVM finality. diff --git a/core/services/relay/evm/commit_provider.go b/core/services/relay/evm/commit_provider.go index 71ac3846395..1a9260120f3 100644 --- a/core/services/relay/evm/commit_provider.go +++ b/core/services/relay/evm/commit_provider.go @@ -114,7 +114,7 @@ func (p *SrcCommitProvider) Close() error { if p.seenOnRampAddress == nil { return nil } - return ccip.CloseOnRampReader(p.lggr, versionFinder, *p.seenSourceChainSelector, *p.seenDestChainSelector, *p.seenOnRampAddress, p.lp, p.client) + return ccip.CloseOnRampReader(context.Background(), p.lggr, versionFinder, *p.seenSourceChainSelector, *p.seenDestChainSelector, *p.seenOnRampAddress, p.lp, p.client) }) var multiErr error @@ -165,25 +165,26 @@ func (p *DstCommitProvider) Name() string { } func (p *DstCommitProvider) Close() error { + ctx := context.Background() versionFinder := ccip.NewEvmVersionFinder() - unregisterFuncs := make([]func() error, 0, 2) - unregisterFuncs = append(unregisterFuncs, func() error { + unregisterFuncs := make([]func(ctx context.Context) error, 0, 2) + unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error { if p.seenCommitStoreAddress == nil { return nil } - return ccip.CloseCommitStoreReader(p.lggr, versionFinder, *p.seenCommitStoreAddress, p.client, p.lp) + return ccip.CloseCommitStoreReader(ctx, p.lggr, versionFinder, *p.seenCommitStoreAddress, p.client, p.lp) }) - unregisterFuncs = append(unregisterFuncs, func() error { + unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error { if p.seenOffRampAddress == nil { return nil } - return ccip.CloseOffRampReader(p.lggr, versionFinder, *p.seenOffRampAddress, p.client, p.lp, nil, big.NewInt(0)) + return ccip.CloseOffRampReader(ctx, p.lggr, versionFinder, *p.seenOffRampAddress, p.client, p.lp, nil, big.NewInt(0)) }) var multiErr error for _, fn := range unregisterFuncs { - if err := fn(); err != nil { + if err := fn(ctx); err != nil { multiErr = multierr.Append(multiErr, err) } } @@ -257,7 +258,7 @@ func (p *DstCommitProvider) NewCommitStoreReader(ctx context.Context, commitStor p.seenCommitStoreAddress = &commitStoreAddress versionFinder := ccip.NewEvmVersionFinder() - commitStoreReader, err = NewIncompleteDestCommitStoreReader(p.lggr, versionFinder, commitStoreAddress, p.client, p.lp) + commitStoreReader, err = NewIncompleteDestCommitStoreReader(ctx, p.lggr, versionFinder, commitStoreAddress, p.client, p.lp) return } @@ -267,7 +268,7 @@ func (p *SrcCommitProvider) NewOnRampReader(ctx context.Context, onRampAddress c p.seenDestChainSelector = &destChainSelector versionFinder := ccip.NewEvmVersionFinder() - onRampReader, err = ccip.NewOnRampReader(p.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, p.lp, p.client) + onRampReader, err = ccip.NewOnRampReader(ctx, p.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, p.lp, p.client) return } @@ -280,7 +281,7 @@ func (p *SrcCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cc } func (p *DstCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { - offRampReader, err = ccip.NewOffRampReader(p.lggr, p.versionFinder, offRampAddr, p.client, p.lp, p.gasEstimator, &p.maxGasPrice, true) + offRampReader, err = ccip.NewOffRampReader(ctx, p.lggr, p.versionFinder, offRampAddr, p.client, p.lp, p.gasEstimator, &p.maxGasPrice, true) return } diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 43b8408d2ee..7c380211ea0 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -572,7 +572,7 @@ func (r *Relayer) NewLLOProvider(ctx context.Context, rargs commontypes.RelayArg } configuratorAddress := common.HexToAddress(relayOpts.ContractID) - return NewLLOProvider(context.Background(), transmitter, r.lggr, r.retirementReportCache, r.chain, configuratorAddress, cdc, relayConfig, relayOpts) + return NewLLOProvider(ctx, transmitter, r.lggr, r.retirementReportCache, r.chain, configuratorAddress, cdc, relayConfig, relayOpts) } func (r *Relayer) NewFunctionsProvider(ctx context.Context, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.FunctionsProvider, error) { @@ -1022,6 +1022,7 @@ func (r *Relayer) NewCCIPExecProvider(ctx context.Context, rargs commontypes.Rel // bail early. if execPluginConfig.IsSourceProvider { return NewSrcExecProvider( + ctx, r.lggr, versionFinder, r.chain.Client(), diff --git a/core/services/relay/evm/evmtesting/chain_components_interface_tester.go b/core/services/relay/evm/evmtesting/chain_components_interface_tester.go index 968c7667882..c0d1754f6fd 100644 --- a/core/services/relay/evm/evmtesting/chain_components_interface_tester.go +++ b/core/services/relay/evm/evmtesting/chain_components_interface_tester.go @@ -146,6 +146,7 @@ func (it *EVMChainComponentsInterfaceTester[T]) Setup(t T) { "OracleID": {Name: "oracleId"}, // this is just to illustrate an example, generic names shouldn't really be formatted like this since other chains might not store it in the same way "NestedStaticStruct.Inner.IntVal": {Name: "nestedStaticStruct.Inner.IntVal"}, + "NestedDynamicStruct.FixedBytes": {Name: "nestedDynamicStruct.FixedBytes"}, "BigField": {Name: "bigField"}, }, }, diff --git a/core/services/relay/evm/evmtesting/run_tests.go b/core/services/relay/evm/evmtesting/run_tests.go index 1e3d6ec8edc..2efe1c3f08a 100644 --- a/core/services/relay/evm/evmtesting/run_tests.go +++ b/core/services/relay/evm/evmtesting/run_tests.go @@ -223,6 +223,26 @@ func RunContractReaderInLoopTests[T TestingT[T]](t T, it ChainComponentsInterfac }, it.MaxWaitTimeForEvents(), time.Millisecond*10) }) + t.Run("Filtering can be done on data words using value comparator on a static field in a dynamic struct that is the first dynamic field", func(t T) { + ts := &TestStruct{} + assert.Eventually(t, func() bool { + sequences, err := cr.QueryKey(ctx, boundContract, query.KeyFilter{Key: EventName, Expressions: []query.Expression{ + query.Comparator("OracleID", + primitives.ValueComparator{ + Value: uint8(ts2.OracleID), + Operator: primitives.Eq, + }), + query.Comparator("NestedDynamicStruct.FixedBytes", + primitives.ValueComparator{ + Value: ts2.NestedDynamicStruct.FixedBytes, + Operator: primitives.Eq, + }), + }, + }, query.LimitAndSort{}, ts) + return err == nil && len(sequences) == 1 && reflect.DeepEqual(&ts2, sequences[0].Data) + }, it.MaxWaitTimeForEvents(), time.Millisecond*10) + }) + t.Run("Filtering can be done on data words using value comparators on fields that require manual index input", func(t T) { empty12Bytes := [12]byte{} val1, val2, val3, val4 := uint32(1), uint32(2), uint32(3), uint64(4) diff --git a/core/services/relay/evm/exec_provider.go b/core/services/relay/evm/exec_provider.go index 98f85c23aa8..da190d20356 100644 --- a/core/services/relay/evm/exec_provider.go +++ b/core/services/relay/evm/exec_provider.go @@ -48,6 +48,7 @@ type SrcExecProvider struct { } func NewSrcExecProvider( + ctx context.Context, lggr logger.Logger, versionFinder ccip.VersionFinder, client client.Client, @@ -64,7 +65,7 @@ func NewSrcExecProvider( var usdcReader *ccip.USDCReaderImpl var err error if usdcAttestationAPI != "" { - usdcReader, err = ccip.NewUSDCReader(lggr, jobID, usdcSrcMsgTransmitterAddr, lp, true) + usdcReader, err = ccip.NewUSDCReader(ctx, lggr, jobID, usdcSrcMsgTransmitterAddr, lp, true) if err != nil { return nil, fmt.Errorf("new usdc reader: %w", err) } @@ -100,25 +101,26 @@ func (s *SrcExecProvider) Start(ctx context.Context) error { // Close is called when the job that created this provider is closed. func (s *SrcExecProvider) Close() error { + ctx := context.Background() versionFinder := ccip.NewEvmVersionFinder() - unregisterFuncs := make([]func() error, 0, 2) - unregisterFuncs = append(unregisterFuncs, func() error { + unregisterFuncs := make([]func(context.Context) error, 0, 2) + unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error { // avoid panic in the case NewOnRampReader wasn't called if s.seenOnRampAddress == nil { return nil } - return ccip.CloseOnRampReader(s.lggr, versionFinder, *s.seenSourceChainSelector, *s.seenDestChainSelector, *s.seenOnRampAddress, s.lp, s.client) + return ccip.CloseOnRampReader(ctx, s.lggr, versionFinder, *s.seenSourceChainSelector, *s.seenDestChainSelector, *s.seenOnRampAddress, s.lp, s.client) }) - unregisterFuncs = append(unregisterFuncs, func() error { + unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error { if s.usdcAttestationAPI == "" { return nil } - return ccip.CloseUSDCReader(s.lggr, s.lggr.Name(), s.usdcSrcMsgTransmitterAddr, s.lp) + return ccip.CloseUSDCReader(ctx, s.lggr, s.lggr.Name(), s.usdcSrcMsgTransmitterAddr, s.lp) }) var multiErr error for _, fn := range unregisterFuncs { - if err := fn(); err != nil { + if err := fn(ctx); err != nil { multiErr = multierr.Append(multiErr, err) } } @@ -176,7 +178,7 @@ func (s *SrcExecProvider) NewOnRampReader(ctx context.Context, onRampAddress cci s.seenOnRampAddress = &onRampAddress versionFinder := ccip.NewEvmVersionFinder() - onRampReader, err = ccip.NewOnRampReader(s.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, s.lp, s.client) + onRampReader, err = ccip.NewOnRampReader(ctx, s.lggr, versionFinder, sourceChainSelector, destChainSelector, onRampAddress, s.lp, s.client) return } @@ -289,22 +291,23 @@ func (d *DstExecProvider) Start(ctx context.Context) error { // If NewOnRampReader and NewCommitStoreReader have not been called, their corresponding // Close methods will be expected to error. func (d *DstExecProvider) Close() error { + ctx := context.Background() versionFinder := ccip.NewEvmVersionFinder() - unregisterFuncs := make([]func() error, 0, 2) - unregisterFuncs = append(unregisterFuncs, func() error { + unregisterFuncs := make([]func(context.Context) error, 0, 2) + unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error { if d.seenCommitStoreAddr == nil { return nil } - return ccip.CloseCommitStoreReader(d.lggr, versionFinder, *d.seenCommitStoreAddr, d.client, d.lp) + return ccip.CloseCommitStoreReader(ctx, d.lggr, versionFinder, *d.seenCommitStoreAddr, d.client, d.lp) }) - unregisterFuncs = append(unregisterFuncs, func() error { - return ccip.CloseOffRampReader(d.lggr, versionFinder, d.offRampAddress, d.client, d.lp, nil, big.NewInt(0)) + unregisterFuncs = append(unregisterFuncs, func(ctx context.Context) error { + return ccip.CloseOffRampReader(ctx, d.lggr, versionFinder, d.offRampAddress, d.client, d.lp, nil, big.NewInt(0)) }) var multiErr error for _, fn := range unregisterFuncs { - if err := fn(); err != nil { + if err := fn(ctx); err != nil { multiErr = multierr.Append(multiErr, err) } } @@ -347,12 +350,12 @@ func (d *DstExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptyp d.seenCommitStoreAddr = &addr versionFinder := ccip.NewEvmVersionFinder() - commitStoreReader, err = NewIncompleteDestCommitStoreReader(d.lggr, versionFinder, addr, d.client, d.lp) + commitStoreReader, err = NewIncompleteDestCommitStoreReader(ctx, d.lggr, versionFinder, addr, d.client, d.lp) return } func (d *DstExecProvider) NewOffRampReader(ctx context.Context, offRampAddress cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { - offRampReader, err = ccip.NewOffRampReader(d.lggr, d.versionFinder, offRampAddress, d.client, d.lp, d.gasEstimator, &d.maxGasPrice, true) + offRampReader, err = ccip.NewOffRampReader(ctx, d.lggr, d.versionFinder, offRampAddress, d.client, d.lp, d.gasEstimator, &d.maxGasPrice, true) return } diff --git a/core/services/relay/evm/mercury/persistence_manager.go b/core/services/relay/evm/mercury/persistence_manager.go index dfe75e7c3ce..68137d04c14 100644 --- a/core/services/relay/evm/mercury/persistence_manager.go +++ b/core/services/relay/evm/mercury/persistence_manager.go @@ -87,7 +87,7 @@ func (pm *PersistenceManager) Load(ctx context.Context) ([]*Transmission, error) func (pm *PersistenceManager) runFlushDeletesLoop() { defer pm.wg.Done() - ctx, cancel := pm.stopCh.Ctx(context.Background()) + ctx, cancel := pm.stopCh.NewCtx() defer cancel() ticker := services.NewTicker(pm.flushDeletesFrequency) diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index b55cc8cf028..4e57a3d07cf 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -179,7 +179,7 @@ func (s *server) HealthReport() map[string]error { func (s *server) runDeleteQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup) { defer wg.Done() - runloopCtx, cancel := stopCh.Ctx(context.Background()) + ctx, cancel := stopCh.NewCtx() defer cancel() // Exponential backoff for very rarely occurring errors (DB disconnect etc) @@ -194,7 +194,7 @@ func (s *server) runDeleteQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup select { case req := <-s.deleteQueue: for { - if err := s.pm.Delete(runloopCtx, req); err != nil { + if err := s.pm.Delete(ctx, req); err != nil { s.lggr.Errorw("Failed to delete transmit request record", "err", err, "req.Payload", req.Payload) s.transmitQueueDeleteErrorCount.Inc() select { @@ -227,7 +227,7 @@ func (s *server) runQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup, feed Factor: 2, Jitter: true, } - runloopCtx, cancel := stopCh.Ctx(context.Background()) + ctx, cancel := stopCh.NewCtx() defer cancel() for { t := s.q.BlockingPop() @@ -235,12 +235,13 @@ func (s *server) runQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup, feed // queue was closed return } - ctx, cancel := context.WithTimeout(runloopCtx, utils.WithJitter(s.transmitTimeout)) - res, err := s.c.Transmit(ctx, t.Req) - cancel() - if runloopCtx.Err() != nil { - // runloop context is only canceled on transmitter close so we can - // exit the runloop here + res, err := func(ctx context.Context) (*pb.TransmitResponse, error) { + ctx, cancel := context.WithTimeout(ctx, utils.WithJitter(s.transmitTimeout)) + cancel() + return s.c.Transmit(ctx, t.Req) + }(ctx) + if ctx.Err() != nil { + // only canceled on transmitter close so we can exit return } else if err != nil { s.transmitConnectionErrorCount.Inc() diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index 37207510655..c87b555e6a5 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -189,7 +189,7 @@ func (w *client) resetTransport() { if !ok { panic("resetTransport should never be called unless client is in 'started' state") } - ctx, cancel := w.chStop.Ctx(context.Background()) + ctx, cancel := w.chStop.NewCtx() defer cancel() b := utils.NewRedialBackoff() for { diff --git a/core/services/workflows/monitoring.go b/core/services/workflows/monitoring.go index 4e6f3fc29e8..e2cb4c7259e 100644 --- a/core/services/workflows/monitoring.go +++ b/core/services/workflows/monitoring.go @@ -20,32 +20,32 @@ var workflowStepErrorCounter metric.Int64Counter var engineHeartbeatCounter metric.Int64UpDownCounter func initMonitoringResources() (err error) { - registerTriggerFailureCounter, err = beholder.GetMeter().Int64Counter("platform.engine.register_trigger.failures") + registerTriggerFailureCounter, err = beholder.GetMeter().Int64Counter("platform_engine_registertrigger_failures") if err != nil { return fmt.Errorf("failed to register trigger failure counter: %w", err) } - workflowsRunningGauge, err = beholder.GetMeter().Int64Gauge("platform.engine.workflows.count") + workflowsRunningGauge, err = beholder.GetMeter().Int64Gauge("platform_engine_workflow_count") if err != nil { return fmt.Errorf("failed to register workflows running gauge: %w", err) } - capabilityInvocationCounter, err = beholder.GetMeter().Int64Counter("platform.engine.capabilities_invoked.count") + capabilityInvocationCounter, err = beholder.GetMeter().Int64Counter("platform_engine_capabilities_count") if err != nil { return fmt.Errorf("failed to register capability invocation counter: %w", err) } - workflowExecutionLatencyGauge, err = beholder.GetMeter().Int64Gauge("platform.engine.workflow.time") + workflowExecutionLatencyGauge, err = beholder.GetMeter().Int64Gauge("platform_engine_workflow_time") if err != nil { return fmt.Errorf("failed to register workflow execution latency gauge: %w", err) } - workflowStepErrorCounter, err = beholder.GetMeter().Int64Counter("platform.engine.workflow.errors") + workflowStepErrorCounter, err = beholder.GetMeter().Int64Counter("platform_engine_workflow_errors") if err != nil { return fmt.Errorf("failed to register workflow step error counter: %w", err) } - engineHeartbeatCounter, err = beholder.GetMeter().Int64UpDownCounter("platform.engine.heartbeat") + engineHeartbeatCounter, err = beholder.GetMeter().Int64UpDownCounter("platform_engine_heartbeat") if err != nil { return fmt.Errorf("failed to register engine heartbeat counter: %w", err) } diff --git a/core/sessions/ldapauth/sync.go b/core/sessions/ldapauth/sync.go index 5eeaf051526..e3ac8898101 100644 --- a/core/sessions/ldapauth/sync.go +++ b/core/sessions/ldapauth/sync.go @@ -9,8 +9,8 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/lib/pq" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" - "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/sessions" @@ -22,110 +22,135 @@ type LDAPServerStateSyncer struct { config config.LDAP lggr logger.Logger nextSyncTime time.Time + done chan struct{} + stopCh services.StopChan } -// NewLDAPServerStateSync creates a reaper that cleans stale sessions from the store. -func NewLDAPServerStateSync( +// NewLDAPServerStateSyncer creates a reaper that cleans stale sessions from the store. +func NewLDAPServerStateSyncer( ds sqlutil.DataSource, config config.LDAP, lggr logger.Logger, -) *utils.SleeperTask { - namedLogger := lggr.Named("LDAPServerStateSync") - serverSync := LDAPServerStateSyncer{ - ds: ds, - ldapClient: newLDAPClient(config), - config: config, - lggr: namedLogger, - nextSyncTime: time.Time{}, +) *LDAPServerStateSyncer { + return &LDAPServerStateSyncer{ + ds: ds, + ldapClient: newLDAPClient(config), + config: config, + lggr: lggr.Named("LDAPServerStateSync"), + done: make(chan struct{}), + stopCh: make(services.StopChan), } +} + +func (l *LDAPServerStateSyncer) Name() string { + return l.lggr.Name() +} + +func (l *LDAPServerStateSyncer) Ready() error { return nil } + +func (l *LDAPServerStateSyncer) HealthReport() map[string]error { + return map[string]error{l.Name(): nil} +} + +func (l *LDAPServerStateSyncer) Start(ctx context.Context) error { // If enabled, start a background task that calls the Sync/Work function on an // interval without needing an auth event to trigger it // Use IsInstant to check 0 value to omit functionality. - if !config.UpstreamSyncInterval().IsInstant() { - lggr.Info("LDAP Config UpstreamSyncInterval is non-zero, sync functionality will be called on a timer, respecting the UpstreamSyncRateLimit value") - serverSync.StartWorkOnTimer() + if !l.config.UpstreamSyncInterval().IsInstant() { + l.lggr.Info("LDAP Config UpstreamSyncInterval is non-zero, sync functionality will be called on a timer, respecting the UpstreamSyncRateLimit value") + go l.run() } else { // Ensure upstream server state is synced on startup manually if interval check not set - serverSync.Work() + l.Work(ctx) } - - // Start background Sync call task reactive to auth related events - serverSyncSleeperTask := utils.NewSleeperTask(&serverSync) - return serverSyncSleeperTask + return nil } -func (ldSync *LDAPServerStateSyncer) Name() string { - return "LDAPServerStateSync" +func (l *LDAPServerStateSyncer) Close() error { + close(l.stopCh) + <-l.done + return nil } -func (ldSync *LDAPServerStateSyncer) StartWorkOnTimer() { - time.AfterFunc(ldSync.config.UpstreamSyncInterval().Duration(), ldSync.StartWorkOnTimer) - ldSync.Work() +func (l *LDAPServerStateSyncer) run() { + defer close(l.done) + ctx, cancel := l.stopCh.NewCtx() + defer cancel() + ticker := time.NewTicker(l.config.UpstreamSyncInterval().Duration()) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + l.Work(ctx) + } + } } -func (ldSync *LDAPServerStateSyncer) Work() { - ctx := context.Background() // TODO https://smartcontract-it.atlassian.net/browse/BCF-2887 +func (l *LDAPServerStateSyncer) Work(ctx context.Context) { // Purge expired ldap_sessions and ldap_user_api_tokens - recordCreationStaleThreshold := ldSync.config.SessionTimeout().Before(time.Now()) - err := ldSync.deleteStaleSessions(ctx, recordCreationStaleThreshold) + recordCreationStaleThreshold := l.config.SessionTimeout().Before(time.Now()) + err := l.deleteStaleSessions(ctx, recordCreationStaleThreshold) if err != nil { - ldSync.lggr.Error("unable to expire local LDAP sessions: ", err) + l.lggr.Error("unable to expire local LDAP sessions: ", err) } - recordCreationStaleThreshold = ldSync.config.UserAPITokenDuration().Before(time.Now()) - err = ldSync.deleteStaleAPITokens(ctx, recordCreationStaleThreshold) + recordCreationStaleThreshold = l.config.UserAPITokenDuration().Before(time.Now()) + err = l.deleteStaleAPITokens(ctx, recordCreationStaleThreshold) if err != nil { - ldSync.lggr.Error("unable to expire user API tokens: ", err) + l.lggr.Error("unable to expire user API tokens: ", err) } // Optional rate limiting check to limit the amount of upstream LDAP server queries performed - if !ldSync.config.UpstreamSyncRateLimit().IsInstant() { - if !time.Now().After(ldSync.nextSyncTime) { + if !l.config.UpstreamSyncRateLimit().IsInstant() { + if !time.Now().After(l.nextSyncTime) { return } // Enough time has elapsed to sync again, store the time for when next sync is allowed and begin sync - ldSync.nextSyncTime = time.Now().Add(ldSync.config.UpstreamSyncRateLimit().Duration()) + l.nextSyncTime = time.Now().Add(l.config.UpstreamSyncRateLimit().Duration()) } - ldSync.lggr.Info("Begin Upstream LDAP provider state sync after checking time against config UpstreamSyncInterval and UpstreamSyncRateLimit") + l.lggr.Info("Begin Upstream LDAP provider state sync after checking time against config UpstreamSyncInterval and UpstreamSyncRateLimit") // For each defined role/group, query for the list of group members to gather the full list of possible users users := []sessions.User{} - conn, err := ldSync.ldapClient.CreateEphemeralConnection() + conn, err := l.ldapClient.CreateEphemeralConnection() if err != nil { - ldSync.lggr.Error("Failed to Dial LDAP Server: ", err) + l.lggr.Error("Failed to Dial LDAP Server: ", err) return } // Root level root user auth with credentials provided from config - bindStr := ldSync.config.BaseUserAttr() + "=" + ldSync.config.ReadOnlyUserLogin() + "," + ldSync.config.BaseDN() - if err = conn.Bind(bindStr, ldSync.config.ReadOnlyUserPass()); err != nil { - ldSync.lggr.Error("Unable to login as initial root LDAP user: ", err) + bindStr := l.config.BaseUserAttr() + "=" + l.config.ReadOnlyUserLogin() + "," + l.config.BaseDN() + if err = conn.Bind(bindStr, l.config.ReadOnlyUserPass()); err != nil { + l.lggr.Error("Unable to login as initial root LDAP user: ", err) } defer conn.Close() // Query for list of uniqueMember IDs present in Admin group - adminUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.AdminUserGroupCN(), sessions.UserRoleAdmin) + adminUsers, err := l.ldapGroupMembersListToUser(conn, l.config.AdminUserGroupCN(), sessions.UserRoleAdmin) if err != nil { - ldSync.lggr.Error("Error in ldapGroupMembersListToUser: ", err) + l.lggr.Error("Error in ldapGroupMembersListToUser: ", err) return } // Query for list of uniqueMember IDs present in Edit group - editUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.EditUserGroupCN(), sessions.UserRoleEdit) + editUsers, err := l.ldapGroupMembersListToUser(conn, l.config.EditUserGroupCN(), sessions.UserRoleEdit) if err != nil { - ldSync.lggr.Error("Error in ldapGroupMembersListToUser: ", err) + l.lggr.Error("Error in ldapGroupMembersListToUser: ", err) return } // Query for list of uniqueMember IDs present in Edit group - runUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.RunUserGroupCN(), sessions.UserRoleRun) + runUsers, err := l.ldapGroupMembersListToUser(conn, l.config.RunUserGroupCN(), sessions.UserRoleRun) if err != nil { - ldSync.lggr.Error("Error in ldapGroupMembersListToUser: ", err) + l.lggr.Error("Error in ldapGroupMembersListToUser: ", err) return } // Query for list of uniqueMember IDs present in Edit group - readUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.ReadUserGroupCN(), sessions.UserRoleView) + readUsers, err := l.ldapGroupMembersListToUser(conn, l.config.ReadUserGroupCN(), sessions.UserRoleView) if err != nil { - ldSync.lggr.Error("Error in ldapGroupMembersListToUser: ", err) + l.lggr.Error("Error in ldapGroupMembersListToUser: ", err) return } @@ -147,9 +172,9 @@ func (ldSync *LDAPServerStateSyncer) Work() { // For each unique user in list of active sessions, check for 'Is Active' propery if defined in the config. Some LDAP providers // list group members that are no longer marked as active - usersActiveFlags, err := ldSync.validateUsersActive(dedupedEmails, conn) + usersActiveFlags, err := l.validateUsersActive(dedupedEmails, conn) if err != nil { - ldSync.lggr.Error("Error validating supplied user list: ", err) + l.lggr.Error("Error validating supplied user list: ", err) } // Remove users in the upstreamUserStateMap source of truth who are part of groups but marked as deactivated/no-active for i, active := range usersActiveFlags { @@ -160,7 +185,7 @@ func (ldSync *LDAPServerStateSyncer) Work() { // upstreamUserStateMap is now the most up to date source of truth // Now sync database sessions and roles with new data - err = sqlutil.TransactDataSource(ctx, ldSync.ds, nil, func(tx sqlutil.DataSource) error { + err = sqlutil.TransactDataSource(ctx, l.ds, nil, func(tx sqlutil.DataSource) error { // First, purge users present in the local ldap_sessions table but not in the upstream server type LDAPSession struct { UserEmail string @@ -248,36 +273,36 @@ func (ldSync *LDAPServerStateSyncer) Work() { } } - ldSync.lggr.Info("local ldap_sessions and ldap_user_api_tokens table successfully synced with upstream LDAP state") + l.lggr.Info("local ldap_sessions and ldap_user_api_tokens table successfully synced with upstream LDAP state") return nil }) if err != nil { - ldSync.lggr.Error("Error syncing local database state: ", err) + l.lggr.Error("Error syncing local database state: ", err) } - ldSync.lggr.Info("Upstream LDAP sync complete") + l.lggr.Info("Upstream LDAP sync complete") } // deleteStaleSessions deletes all ldap_sessions before the passed time. -func (ldSync *LDAPServerStateSyncer) deleteStaleSessions(ctx context.Context, before time.Time) error { - _, err := ldSync.ds.ExecContext(ctx, "DELETE FROM ldap_sessions WHERE created_at < $1", before) +func (l *LDAPServerStateSyncer) deleteStaleSessions(ctx context.Context, before time.Time) error { + _, err := l.ds.ExecContext(ctx, "DELETE FROM ldap_sessions WHERE created_at < $1", before) return err } // deleteStaleAPITokens deletes all ldap_user_api_tokens before the passed time. -func (ldSync *LDAPServerStateSyncer) deleteStaleAPITokens(ctx context.Context, before time.Time) error { - _, err := ldSync.ds.ExecContext(ctx, "DELETE FROM ldap_user_api_tokens WHERE created_at < $1", before) +func (l *LDAPServerStateSyncer) deleteStaleAPITokens(ctx context.Context, before time.Time) error { + _, err := l.ds.ExecContext(ctx, "DELETE FROM ldap_user_api_tokens WHERE created_at < $1", before) return err } // ldapGroupMembersListToUser queries the LDAP server given a conn for a list of uniqueMember who are part of the parameterized group -func (ldSync *LDAPServerStateSyncer) ldapGroupMembersListToUser(conn LDAPConn, groupNameCN string, roleToAssign sessions.UserRole) ([]sessions.User, error) { +func (l *LDAPServerStateSyncer) ldapGroupMembersListToUser(conn LDAPConn, groupNameCN string, roleToAssign sessions.UserRole) ([]sessions.User, error) { users, err := ldapGroupMembersListToUser( - conn, groupNameCN, roleToAssign, ldSync.config.GroupsDN(), - ldSync.config.BaseDN(), ldSync.config.QueryTimeout(), - ldSync.lggr, + conn, groupNameCN, roleToAssign, l.config.GroupsDN(), + l.config.BaseDN(), l.config.QueryTimeout(), + l.lggr, ) if err != nil { - ldSync.lggr.Errorf("Error listing members of group (%s): %v", groupNameCN, err) + l.lggr.Errorf("Error listing members of group (%s): %v", groupNameCN, err) return users, errors.New("error searching group members in LDAP directory") } return users, nil @@ -286,10 +311,10 @@ func (ldSync *LDAPServerStateSyncer) ldapGroupMembersListToUser(conn LDAPConn, g // validateUsersActive performs an additional LDAP server query for the supplied emails, checking the // returned user data for an 'active' property defined optionally in the config. // Returns same length bool 'valid' array, order preserved -func (ldSync *LDAPServerStateSyncer) validateUsersActive(emails []string, conn LDAPConn) ([]bool, error) { +func (l *LDAPServerStateSyncer) validateUsersActive(emails []string, conn LDAPConn) ([]bool, error) { validUsers := make([]bool, len(emails)) // If active attribute to check is not defined in config, skip - if ldSync.config.ActiveAttribute() == "" { + if l.config.ActiveAttribute() == "" { // pre fill with valids for i := range emails { validUsers[i] = true @@ -301,22 +326,22 @@ func (ldSync *LDAPServerStateSyncer) validateUsersActive(emails []string, conn L filterQuery := "(|" for _, email := range emails { escapedEmail := ldap.EscapeFilter(email) - filterQuery = fmt.Sprintf("%s(%s=%s)", filterQuery, ldSync.config.BaseUserAttr(), escapedEmail) + filterQuery = fmt.Sprintf("%s(%s=%s)", filterQuery, l.config.BaseUserAttr(), escapedEmail) } filterQuery = fmt.Sprintf("(&%s))", filterQuery) - searchBaseDN := fmt.Sprintf("%s,%s", ldSync.config.UsersDN(), ldSync.config.BaseDN()) + searchBaseDN := fmt.Sprintf("%s,%s", l.config.UsersDN(), l.config.BaseDN()) searchRequest := ldap.NewSearchRequest( searchBaseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, - 0, int(ldSync.config.QueryTimeout().Seconds()), false, + 0, int(l.config.QueryTimeout().Seconds()), false, filterQuery, - []string{ldSync.config.BaseUserAttr(), ldSync.config.ActiveAttribute()}, + []string{l.config.BaseUserAttr(), l.config.ActiveAttribute()}, nil, ) // Query LDAP server for the ActiveAttribute property of each specified user results, err := conn.Search(searchRequest) if err != nil { - ldSync.lggr.Errorf("Error searching user in LDAP query: %v", err) + l.lggr.Errorf("Error searching user in LDAP query: %v", err) return validUsers, errors.New("error searching users in LDAP directory") } // Ensure user response entries @@ -328,9 +353,9 @@ func (ldSync *LDAPServerStateSyncer) validateUsersActive(emails []string, conn L // keyed on email for final step to return flag bool list where order is preserved emailToActiveMap := make(map[string]bool) for _, result := range results.Entries { - isActiveAttribute := result.GetAttributeValue(ldSync.config.ActiveAttribute()) - uidAttribute := result.GetAttributeValue(ldSync.config.BaseUserAttr()) - emailToActiveMap[uidAttribute] = isActiveAttribute == ldSync.config.ActiveAttributeAllowedValue() + isActiveAttribute := result.GetAttributeValue(l.config.ActiveAttribute()) + uidAttribute := result.GetAttributeValue(l.config.BaseUserAttr()) + emailToActiveMap[uidAttribute] = isActiveAttribute == l.config.ActiveAttributeAllowedValue() } for i, email := range emails { active, ok := emailToActiveMap[email] diff --git a/core/sessions/localauth/reaper.go b/core/sessions/localauth/reaper.go index a3ba1693765..6f2bfe732c5 100644 --- a/core/sessions/localauth/reaper.go +++ b/core/sessions/localauth/reaper.go @@ -23,19 +23,16 @@ type SessionReaperConfig interface { // NewSessionReaper creates a reaper that cleans stale sessions from the store. func NewSessionReaper(ds sqlutil.DataSource, config SessionReaperConfig, lggr logger.Logger) *utils.SleeperTask { - return utils.NewSleeperTask(&sessionReaper{ + return utils.NewSleeperTaskCtx(&sessionReaper{ ds, config, lggr.Named("SessionReaper"), }) } -func (sr *sessionReaper) Name() string { - return "SessionReaper" -} +func (sr *sessionReaper) Name() string { return sr.lggr.Name() } -func (sr *sessionReaper) Work() { - ctx := context.Background() // TODO https://smartcontract-it.atlassian.net/browse/BCF-2887 +func (sr *sessionReaper) Work(ctx context.Context) { recordCreationStaleThreshold := sr.config.SessionReaperExpiration().Before( sr.config.SessionTimeout().Before(time.Now())) err := sr.deleteStaleSessions(ctx, recordCreationStaleThreshold) diff --git a/core/web/resolver/spec.go b/core/web/resolver/spec.go index 00b2442acab..ce23df49264 100644 --- a/core/web/resolver/spec.go +++ b/core/web/resolver/spec.go @@ -1,6 +1,8 @@ package resolver import ( + "fmt" + "github.com/graph-gophers/graphql-go" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -133,6 +135,14 @@ func (r *SpecResolver) ToStandardCapabilitiesSpec() (*StandardCapabilitiesSpecRe return &StandardCapabilitiesSpecResolver{spec: *r.j.StandardCapabilitiesSpec}, true } +func (r *SpecResolver) ToStreamSpec() (*StreamSpecResolver, bool) { + if r.j.Type != job.Stream { + return nil, false + } + + return &StreamSpecResolver{streamID: fmt.Sprintf("%d", r.j.StreamID)}, true +} + type CronSpecResolver struct { spec job.CronSpec } @@ -1045,3 +1055,11 @@ func (r *StandardCapabilitiesSpecResolver) Command() string { func (r *StandardCapabilitiesSpecResolver) Config() *string { return &r.spec.Config } + +type StreamSpecResolver struct { + streamID string +} + +func (r *StreamSpecResolver) StreamID() string { + return r.streamID +} diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index ebdafb22c5e..8fbe07f97f9 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -498,6 +498,7 @@ ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' BlockHistoryPollPeriod = '5s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 200000 EstimateComputeUnitLimit = false diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 2a0ebcf1039..adc1394e654 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -660,6 +660,7 @@ ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' BlockHistoryPollPeriod = '5s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 200000 EstimateComputeUnitLimit = false @@ -703,6 +704,7 @@ ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' BlockHistoryPollPeriod = '5s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 200000 EstimateComputeUnitLimit = false diff --git a/core/web/schema/type/spec.graphql b/core/web/schema/type/spec.graphql index 5a803e2f8ee..db81001543c 100644 --- a/core/web/schema/type/spec.graphql +++ b/core/web/schema/type/spec.graphql @@ -12,7 +12,8 @@ union JobSpec = BootstrapSpec | GatewaySpec | WorkflowSpec | - StandardCapabilitiesSpec + StandardCapabilitiesSpec | + StreamSpec type CronSpec { schedule: String! @@ -178,4 +179,8 @@ type StandardCapabilitiesSpec { createdAt: Time! command: String! config: String -} \ No newline at end of file +} + +type StreamSpec { + streamID: String! +} diff --git a/core/web/solana_chains_controller_test.go b/core/web/solana_chains_controller_test.go index 148d6302592..56605f734aa 100644 --- a/core/web/solana_chains_controller_test.go +++ b/core/web/solana_chains_controller_test.go @@ -58,6 +58,7 @@ ComputeUnitPriceMin = 0 ComputeUnitPriceDefault = 0 FeeBumpPeriod = '3s' BlockHistoryPollPeriod = '5s' +BlockHistorySize = 1 ComputeUnitLimitDefault = 200000 EstimateComputeUnitLimit = false Nodes = [] diff --git a/deployment/ccip/active_candidate.go b/deployment/ccip/active_candidate.go new file mode 100644 index 00000000000..c65dac04103 --- /dev/null +++ b/deployment/ccip/active_candidate.go @@ -0,0 +1,139 @@ +package ccipdeployment + +import ( + "fmt" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" + "github.com/smartcontractkit/chainlink/deployment" + cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "math/big" +) + +// SetCandidateExecPluginOps calls setCandidate on CCIPHome contract through the UpdateDON call on CapReg contract +// This proposes to set up OCR3 config for the provided plugin for the DON +func SetCandidateOnExistingDon( + pluginConfig ccip_home.CCIPHomeOCR3Config, + capReg *capabilities_registry.CapabilitiesRegistry, + ccipHome *ccip_home.CCIPHome, + chainSelector uint64, + nodes deployment.Nodes, +) ([]mcms.Operation, error) { + // fetch DON ID for the chain + donID, err := DonIDForChain(capReg, ccipHome, chainSelector) + if err != nil { + return nil, fmt.Errorf("fetch don id for chain: %w", err) + } + fmt.Printf("donID: %d", donID) + encodedSetCandidateCall, err := CCIPHomeABI.Pack( + "setCandidate", + donID, + pluginConfig.PluginType, + pluginConfig, + [32]byte{}, + ) + if err != nil { + return nil, fmt.Errorf("pack set candidate call: %w", err) + } + + // set candidate call + updateDonTx, err := capReg.UpdateDON( + deployment.SimTransactOpts(), + donID, + nodes.PeerIDs(), + []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: CCIPCapabilityID, + Config: encodedSetCandidateCall, + }, + }, + false, + nodes.DefaultF(), + ) + if err != nil { + return nil, fmt.Errorf("update don w/ exec config: %w", err) + } + + return []mcms.Operation{{ + To: capReg.Address(), + Data: updateDonTx.Data(), + Value: big.NewInt(0), + }}, nil +} + +// PromoteCandidateOp will create the MCMS Operation for `promoteCandidateAndRevokeActive` directed towards the capabilityRegistry +func PromoteCandidateOp(donID uint32, pluginType uint8, capReg *capabilities_registry.CapabilitiesRegistry, + ccipHome *ccip_home.CCIPHome, nodes deployment.Nodes) (mcms.Operation, error) { + + allConfigs, err := ccipHome.GetAllConfigs(nil, donID, pluginType) + if err != nil { + return mcms.Operation{}, err + } + + if allConfigs.CandidateConfig.ConfigDigest == [32]byte{} { + return mcms.Operation{}, fmt.Errorf("candidate digest is empty, expected nonempty") + } + fmt.Printf("commit candidate digest after setCandidate: %x\n", allConfigs.CandidateConfig.ConfigDigest) + + encodedPromotionCall, err := CCIPHomeABI.Pack( + "promoteCandidateAndRevokeActive", + donID, + pluginType, + allConfigs.CandidateConfig.ConfigDigest, + allConfigs.ActiveConfig.ConfigDigest, + ) + if err != nil { + return mcms.Operation{}, fmt.Errorf("pack promotion call: %w", err) + } + + updateDonTx, err := capReg.UpdateDON( + deployment.SimTransactOpts(), + donID, + nodes.PeerIDs(), + []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: CCIPCapabilityID, + Config: encodedPromotionCall, + }, + }, + false, + nodes.DefaultF(), + ) + if err != nil { + return mcms.Operation{}, fmt.Errorf("error creating updateDon op for donID(%d) and plugin type (%d): %w", donID, pluginType, err) + } + return mcms.Operation{ + To: capReg.Address(), + Data: updateDonTx.Data(), + Value: big.NewInt(0), + }, nil +} + +// PromoteAllCandidatesForChainOps promotes the candidate commit and exec configs to active by calling promoteCandidateAndRevokeActive on CCIPHome through the UpdateDON call on CapReg contract +func PromoteAllCandidatesForChainOps( + capReg *capabilities_registry.CapabilitiesRegistry, + ccipHome *ccip_home.CCIPHome, + chainSelector uint64, + nodes deployment.Nodes, +) ([]mcms.Operation, error) { + // fetch DON ID for the chain + donID, err := DonIDForChain(capReg, ccipHome, chainSelector) + if err != nil { + return nil, fmt.Errorf("fetch don id for chain: %w", err) + } + + var mcmsOps []mcms.Operation + updateCommitOp, err := PromoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPCommit), capReg, ccipHome, nodes) + if err != nil { + return nil, fmt.Errorf("promote candidate op: %w", err) + } + mcmsOps = append(mcmsOps, updateCommitOp) + + updateExecOp, err := PromoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPExec), capReg, ccipHome, nodes) + if err != nil { + return nil, fmt.Errorf("promote candidate op: %w", err) + } + mcmsOps = append(mcmsOps, updateExecOp) + + return mcmsOps, nil +} diff --git a/deployment/ccip/add_lane_test.go b/deployment/ccip/add_lane_test.go index de47aa8e627..d8443ad288b 100644 --- a/deployment/ccip/add_lane_test.go +++ b/deployment/ccip/add_lane_test.go @@ -17,6 +17,7 @@ import ( // TestAddLane covers the workflow of adding a lane between two chains and enabling it. // It also covers the case where the onRamp is disabled on the OffRamp contract initially and then enabled. func TestAddLane(t *testing.T) { + t.Parallel() // We add more chains to the chainlink nodes than the number of chains where CCIP is deployed. e := NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 4, 4) // Here we have CR + nodes set up, but no CCIP contracts deployed. @@ -93,7 +94,7 @@ func TestAddLane(t *testing.T) { startBlock := latesthdr.Number.Uint64() // Send traffic on the first lane and it should not be processed by the plugin as onRamp is disabled // we will check this by confirming that the message is not executed by the end of the test - seqNum1 := TestSendRequest(t, e.Env, state, chain1, chain2, false) + seqNum1 := TestSendRequest(t, e.Env, state, chain1, chain2, false, nil) require.Equal(t, uint64(1), seqNum1) // Add another lane @@ -103,7 +104,7 @@ func TestAddLane(t *testing.T) { latesthdr, err = e.Env.Chains[chain1].Client.HeaderByNumber(testcontext.Get(t), nil) require.NoError(t, err) startBlock2 := latesthdr.Number.Uint64() - seqNum2 := TestSendRequest(t, e.Env, state, chain2, chain1, false) + seqNum2 := TestSendRequest(t, e.Env, state, chain2, chain1, false, nil) require.Equal(t, uint64(1), seqNum2) require.NoError(t, ConfirmExecWithSeqNr(t, e.Env.Chains[chain2], e.Env.Chains[chain1], state.Chains[chain1].OffRamp, &startBlock2, seqNum2)) diff --git a/deployment/ccip/changeset/active_candidate.go b/deployment/ccip/changeset/active_candidate.go index bf061224561..e1d67720d12 100644 --- a/deployment/ccip/changeset/active_candidate.go +++ b/deployment/ccip/changeset/active_candidate.go @@ -2,171 +2,47 @@ package changeset import ( "fmt" - "math/big" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" ) -// SetCandidateExecPluginOps calls setCandidate on CCIPHome contract through the UpdateDON call on CapReg contract -// This proposes to set up OCR3 config for the provided plugin for the DON -func SetCandidateOnExistingDon( - pluginConfig ccip_home.CCIPHomeOCR3Config, - capReg *capabilities_registry.CapabilitiesRegistry, - ccipHome *ccip_home.CCIPHome, - chainSelector uint64, - nodes deployment.Nodes, -) ([]mcms.Operation, error) { - // fetch DON ID for the chain - donID, err := ccdeploy.DonIDForChain(capReg, ccipHome, chainSelector) - if err != nil { - return nil, fmt.Errorf("fetch don id for chain: %w", err) - } - fmt.Printf("donID: %d", donID) - encodedSetCandidateCall, err := ccdeploy.CCIPHomeABI.Pack( - "setCandidate", - donID, - pluginConfig.PluginType, - pluginConfig, - [32]byte{}, - ) - if err != nil { - return nil, fmt.Errorf("pack set candidate call: %w", err) - } - - // set candidate call - updateDonTx, err := capReg.UpdateDON( - deployment.SimTransactOpts(), - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: ccdeploy.CCIPCapabilityID, - Config: encodedSetCandidateCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return nil, fmt.Errorf("update don w/ exec config: %w", err) - } - - return []mcms.Operation{{ - To: capReg.Address(), - Data: updateDonTx.Data(), - Value: big.NewInt(0), - }}, nil -} - -// PromoteCandidateOp will create the MCMS Operation for `promoteCandidateAndRevokeActive` directed towards the capabilityRegistry -func PromoteCandidateOp(donID uint32, pluginType uint8, capReg *capabilities_registry.CapabilitiesRegistry, - ccipHome *ccip_home.CCIPHome, nodes deployment.Nodes) (mcms.Operation, error) { - - allConfigs, err := ccipHome.GetAllConfigs(nil, donID, pluginType) - if err != nil { - return mcms.Operation{}, err - } - - if allConfigs.CandidateConfig.ConfigDigest == [32]byte{} { - return mcms.Operation{}, fmt.Errorf("candidate digest is empty, expected nonempty") - } - fmt.Printf("commit candidate digest after setCandidate: %x\n", allConfigs.CandidateConfig.ConfigDigest) - - encodedPromotionCall, err := ccdeploy.CCIPHomeABI.Pack( - "promoteCandidateAndRevokeActive", - donID, - pluginType, - allConfigs.CandidateConfig.ConfigDigest, - allConfigs.ActiveConfig.ConfigDigest, - ) - if err != nil { - return mcms.Operation{}, fmt.Errorf("pack promotion call: %w", err) - } - - updateDonTx, err := capReg.UpdateDON( - deployment.SimTransactOpts(), - donID, - nodes.PeerIDs(), - []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: ccdeploy.CCIPCapabilityID, - Config: encodedPromotionCall, - }, - }, - false, - nodes.DefaultF(), - ) - if err != nil { - return mcms.Operation{}, fmt.Errorf("error creating updateDon op for donID(%d) and plugin type (%d): %w", donID, pluginType, err) - } - return mcms.Operation{ - To: capReg.Address(), - Data: updateDonTx.Data(), - Value: big.NewInt(0), - }, nil -} - -// PromoteAllCandidatesForChainOps promotes the candidate commit and exec configs to active by calling promoteCandidateAndRevokeActive on CCIPHome through the UpdateDON call on CapReg contract -func PromoteAllCandidatesForChainOps( - capReg *capabilities_registry.CapabilitiesRegistry, - ccipHome *ccip_home.CCIPHome, - chainSelector uint64, - nodes deployment.Nodes, -) ([]mcms.Operation, error) { - // fetch DON ID for the chain - donID, err := ccdeploy.DonIDForChain(capReg, ccipHome, chainSelector) - if err != nil { - return nil, fmt.Errorf("fetch don id for chain: %w", err) - } - - var mcmsOps []mcms.Operation - updateCommitOp, err := PromoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPCommit), capReg, ccipHome, nodes) - if err != nil { - return nil, fmt.Errorf("promote candidate op: %w", err) - } - mcmsOps = append(mcmsOps, updateCommitOp) - - updateExecOp, err := PromoteCandidateOp(donID, uint8(cctypes.PluginTypeCCIPExec), capReg, ccipHome, nodes) - if err != nil { - return nil, fmt.Errorf("promote candidate op: %w", err) - } - mcmsOps = append(mcmsOps, updateExecOp) - - return mcmsOps, nil -} - -// PromoteAllCandidatesProposal generates a proposal to call promoteCandidate on the CCIPHome through CapReg. +// PromoteAllCandidatesChangeset generates a proposal to call promoteCandidate on the CCIPHome through CapReg. // This needs to be called after SetCandidateProposal is executed. -func PromoteAllCandidatesProposal( +func PromoteAllCandidatesChangeset( state ccdeploy.CCIPOnChainState, homeChainSel, newChainSel uint64, nodes deployment.Nodes, -) (*timelock.MCMSWithTimelockProposal, error) { - promoteCandidateOps, err := PromoteAllCandidatesForChainOps( +) (deployment.ChangesetOutput, error) { + promoteCandidateOps, err := ccdeploy.PromoteAllCandidatesForChainOps( state.Chains[homeChainSel].CapabilityRegistry, state.Chains[homeChainSel].CCIPHome, newChainSel, nodes.NonBootstraps(), ) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } - return ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + prop, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ ChainIdentifier: mcms.ChainIdentifier(homeChainSel), Batch: promoteCandidateOps, }}, "promoteCandidate for commit and execution", 0) + if err != nil { + return deployment.ChangesetOutput{}, err + } + return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{ + *prop, + }, + }, nil } // SetCandidateExecPluginProposal calls setCandidate on the CCIPHome for setting up OCR3 exec Plugin config for the new chain. -func SetCandidatePluginProposal( +func SetCandidatePluginChangeset( state ccdeploy.CCIPOnChainState, e deployment.Environment, nodes deployment.Nodes, @@ -174,9 +50,8 @@ func SetCandidatePluginProposal( homeChainSel, feedChainSel, newChainSel uint64, tokenConfig ccdeploy.TokenConfig, pluginType cctypes.PluginType, -) (*timelock.MCMSWithTimelockProposal, error) { +) (deployment.ChangesetOutput, error) { newDONArgs, err := ccdeploy.BuildOCR3ConfigForCCIPHome( - e.Logger, ocrSecrets, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], @@ -186,15 +61,15 @@ func SetCandidatePluginProposal( state.Chains[homeChainSel].RMNHome.Address(), ) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } execConfig, ok := newDONArgs[pluginType] if !ok { - return nil, fmt.Errorf("missing exec plugin in ocr3Configs") + return deployment.ChangesetOutput{}, fmt.Errorf("missing exec plugin in ocr3Configs") } - setCandidateMCMSOps, err := SetCandidateOnExistingDon( + setCandidateMCMSOps, err := ccdeploy.SetCandidateOnExistingDon( execConfig, state.Chains[homeChainSel].CapabilityRegistry, state.Chains[homeChainSel].CCIPHome, @@ -202,11 +77,20 @@ func SetCandidatePluginProposal( nodes.NonBootstraps(), ) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } - return ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + prop, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ ChainIdentifier: mcms.ChainIdentifier(homeChainSel), Batch: setCandidateMCMSOps, }}, "SetCandidate for execution", 0) + if err != nil { + return deployment.ChangesetOutput{}, err + } + return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{ + *prop, + }, + }, nil + } diff --git a/deployment/ccip/changeset/active_candidate_test.go b/deployment/ccip/changeset/active_candidate_test.go index 19f41aa6820..ab27d4c96db 100644 --- a/deployment/ccip/changeset/active_candidate_test.go +++ b/deployment/ccip/changeset/active_candidate_test.go @@ -82,7 +82,7 @@ func TestActiveCandidate(t *testing.T) { require.NoError(t, err) block := latesthdr.Number.Uint64() startBlocks[dest] = &block - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) expectedSeqNum[dest] = seqNum } } @@ -145,7 +145,6 @@ func TestActiveCandidate(t *testing.T) { // commit and exec plugin we will be using rmnHomeAddress := state.Chains[homeCS].RMNHome.Address() ocr3ConfigMap, err := ccdeploy.BuildOCR3ConfigForCCIPHome( - e.Logger, deployment.XXXGenerateTestOCRSecrets(), state.Chains[destCS].OffRamp, e.Chains[destCS], @@ -156,7 +155,7 @@ func TestActiveCandidate(t *testing.T) { ) require.NoError(t, err) - setCommitCandidateOp, err := SetCandidateOnExistingDon( + setCommitCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( ocr3ConfigMap[cctypes.PluginTypeCCIPCommit], state.Chains[homeCS].CapabilityRegistry, state.Chains[homeCS].CCIPHome, @@ -173,7 +172,7 @@ func TestActiveCandidate(t *testing.T) { ccdeploy.ExecuteProposal(t, e, setCommitCandidateSigned, state, homeCS) // create the op for the commit plugin as well - setExecCandidateOp, err := SetCandidateOnExistingDon( + setExecCandidateOp, err := ccdeploy.SetCandidateOnExistingDon( ocr3ConfigMap[cctypes.PluginTypeCCIPExec], state.Chains[homeCS].CapabilityRegistry, state.Chains[homeCS].CCIPHome, @@ -207,7 +206,7 @@ func TestActiveCandidate(t *testing.T) { oldCandidateDigest, err := state.Chains[homeCS].CCIPHome.GetCandidateDigest(nil, donID, uint8(cctypes.PluginTypeCCIPExec)) require.NoError(t, err) - promoteOps, err := PromoteAllCandidatesForChainOps(state.Chains[homeCS].CapabilityRegistry, state.Chains[homeCS].CCIPHome, destCS, nodes.NonBootstraps()) + promoteOps, err := ccdeploy.PromoteAllCandidatesForChainOps(state.Chains[homeCS].CapabilityRegistry, state.Chains[homeCS].CCIPHome, destCS, nodes.NonBootstraps()) require.NoError(t, err) promoteProposal, err := ccdeploy.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ ChainIdentifier: mcms.ChainIdentifier(homeCS), diff --git a/deployment/ccip/changeset/add_chain.go b/deployment/ccip/changeset/add_chain.go index 7f0b096f0a9..aa88e0f112f 100644 --- a/deployment/ccip/changeset/add_chain.go +++ b/deployment/ccip/changeset/add_chain.go @@ -16,15 +16,15 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" ) -// NewChainInboundProposal generates a proposal +// NewChainInboundChangeset generates a proposal // to connect the new chain to the existing chains. -func NewChainInboundProposal( +func NewChainInboundChangeset( e deployment.Environment, state ccipdeployment.CCIPOnChainState, homeChainSel uint64, newChainSel uint64, sources []uint64, -) (*timelock.MCMSWithTimelockProposal, error) { +) (deployment.ChangesetOutput, error) { // Generate proposal which enables new destination (from test router) on all source chains. var batches []timelock.BatchChainOperation for _, source := range sources { @@ -35,7 +35,7 @@ func NewChainInboundProposal( }, }) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } enableFeeQuoterDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( deployment.SimTransactOpts(), @@ -46,7 +46,7 @@ func NewChainInboundProposal( }, }) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: mcms.ChainIdentifier(source), @@ -68,7 +68,7 @@ func NewChainInboundProposal( addChainOp, err := ccipdeployment.ApplyChainConfigUpdatesOp(e, state, homeChainSel, []uint64{newChainSel}) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } batches = append(batches, timelock.BatchChainOperation{ @@ -78,12 +78,19 @@ func NewChainInboundProposal( }, }) - return ccipdeployment.BuildProposalFromBatches(state, batches, "proposal to set new chains", 0) + prop, err := ccipdeployment.BuildProposalFromBatches(state, batches, "proposal to set new chains", 0) + if err != nil { + return deployment.ChangesetOutput{}, err + } + + return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{*prop}, + }, nil } -// AddDonAndSetCandidateProposal adds new DON for destination to home chain +// AddDonAndSetCandidateChangeset adds new DON for destination to home chain // and sets the commit plugin config as candidateConfig for the don. -func AddDonAndSetCandidateProposal( +func AddDonAndSetCandidateChangeset( state ccipdeployment.CCIPOnChainState, e deployment.Environment, nodes deployment.Nodes, @@ -91,9 +98,8 @@ func AddDonAndSetCandidateProposal( homeChainSel, feedChainSel, newChainSel uint64, tokenConfig ccipdeployment.TokenConfig, pluginType types.PluginType, -) (*timelock.MCMSWithTimelockProposal, error) { +) (deployment.ChangesetOutput, error) { newDONArgs, err := ccipdeployment.BuildOCR3ConfigForCCIPHome( - e.Logger, ocrSecrets, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], @@ -103,15 +109,15 @@ func AddDonAndSetCandidateProposal( state.Chains[homeChainSel].RMNHome.Address(), ) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } latestDon, err := ccipdeployment.LatestCCIPDON(state.Chains[homeChainSel].CapabilityRegistry) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } commitConfig, ok := newDONArgs[pluginType] if !ok { - return nil, fmt.Errorf("missing commit plugin in ocr3Configs") + return deployment.ChangesetOutput{}, fmt.Errorf("missing commit plugin in ocr3Configs") } donID := latestDon.Id + 1 addDonOp, err := ccipdeployment.NewDonWithCandidateOp( @@ -120,11 +126,18 @@ func AddDonAndSetCandidateProposal( nodes.NonBootstraps(), ) if err != nil { - return nil, err + return deployment.ChangesetOutput{}, err } - return ccipdeployment.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ + prop, err := ccipdeployment.BuildProposalFromBatches(state, []timelock.BatchChainOperation{{ ChainIdentifier: mcms.ChainIdentifier(homeChainSel), Batch: []mcms.Operation{addDonOp}, }}, "setCandidate for commit and AddDon on new Chain", 0) + if err != nil { + return deployment.ChangesetOutput{}, err + } + + return deployment.ChangesetOutput{ + Proposals: []timelock.MCMSWithTimelockProposal{*prop}, + }, nil } diff --git a/deployment/ccip/changeset/add_chain_test.go b/deployment/ccip/changeset/add_chain_test.go index 69320bd4dac..c0d76875b6c 100644 --- a/deployment/ccip/changeset/add_chain_test.go +++ b/deployment/ccip/changeset/add_chain_test.go @@ -132,34 +132,28 @@ func TestAddChainInbound(t *testing.T) { require.NoError(t, err) // Generate and sign inbound proposal to new 4th chain. - chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, newChain, initialDeploy) + chainInboundChangeset, err := NewChainInboundChangeset(e.Env, state, e.HomeChainSel, newChain, initialDeploy) require.NoError(t, err) - chainInboundExec := ccipdeployment.SignProposal(t, e.Env, chainInboundProposal) - for _, sel := range initialDeploy { - ccipdeployment.ExecuteProposal(t, e.Env, chainInboundExec, state, sel) - } + ccipdeployment.ProcessChangeset(t, e.Env, chainInboundChangeset) + // TODO This currently is not working - Able to send the request here but request gets stuck in execution // Send a new message and expect that this is delivered once the chain is completely set up as inbound //TestSendRequest(t, e.Env, state, initialDeploy[0], newChain, true) t.Logf("Executing add don and set candidate proposal for commit plugin on chain %d", newChain) - addDonProp, err := AddDonAndSetCandidateProposal(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPCommit) + addDonChangeset, err := AddDonAndSetCandidateChangeset(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPCommit) require.NoError(t, err) - - addDonExec := ccipdeployment.SignProposal(t, e.Env, addDonProp) - ccipdeployment.ExecuteProposal(t, e.Env, addDonExec, state, e.HomeChainSel) + ccipdeployment.ProcessChangeset(t, e.Env, addDonChangeset) t.Logf("Executing promote candidate proposal for exec plugin on chain %d", newChain) - setCandidateForExecProposal, err := SetCandidatePluginProposal(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPExec) + setCandidateForExecChangeset, err := SetCandidatePluginChangeset(state, e.Env, nodes, deployment.XXXGenerateTestOCRSecrets(), e.HomeChainSel, e.FeedChainSel, newChain, tokenConfig, types.PluginTypeCCIPExec) require.NoError(t, err) - setCandidateForExecExec := ccipdeployment.SignProposal(t, e.Env, setCandidateForExecProposal) - ccipdeployment.ExecuteProposal(t, e.Env, setCandidateForExecExec, state, e.HomeChainSel) + ccipdeployment.ProcessChangeset(t, e.Env, setCandidateForExecChangeset) t.Logf("Executing promote candidate proposal for both commit and exec plugins on chain %d", newChain) - donPromoteProposal, err := PromoteAllCandidatesProposal(state, e.HomeChainSel, newChain, nodes) + donPromoteChangeset, err := PromoteAllCandidatesChangeset(state, e.HomeChainSel, newChain, nodes) require.NoError(t, err) - donPromoteExec := ccipdeployment.SignProposal(t, e.Env, donPromoteProposal) - ccipdeployment.ExecuteProposal(t, e.Env, donPromoteExec, state, e.HomeChainSel) + ccipdeployment.ProcessChangeset(t, e.Env, donPromoteChangeset) // verify if the configs are updated require.NoError(t, ccipdeployment.ValidateCCIPHomeConfigSetUp( @@ -217,7 +211,7 @@ func TestAddChainInbound(t *testing.T) { latesthdr, err := e.Env.Chains[newChain].Client.HeaderByNumber(testcontext.Get(t), nil) require.NoError(t, err) startBlock := latesthdr.Number.Uint64() - seqNr := ccipdeployment.TestSendRequest(t, e.Env, state, initialDeploy[0], newChain, true) + seqNr := ccipdeployment.TestSendRequest(t, e.Env, state, initialDeploy[0], newChain, true, nil) require.NoError(t, ccipdeployment.ConfirmCommitWithExpectedSeqNumRange(t, e.Env.Chains[initialDeploy[0]], e.Env.Chains[newChain], state.Chains[newChain].OffRamp, &startBlock, cciptypes.SeqNumRange{ cciptypes.SeqNum(1), diff --git a/deployment/ccip/changeset/initial_deploy_test.go b/deployment/ccip/changeset/initial_deploy_test.go index 65b6bf51e62..c172f9f84c8 100644 --- a/deployment/ccip/changeset/initial_deploy_test.go +++ b/deployment/ccip/changeset/initial_deploy_test.go @@ -72,7 +72,7 @@ func TestInitialDeploy(t *testing.T) { require.NoError(t, err) block := latesthdr.Number.Uint64() startBlocks[dest] = &block - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) expectedSeqNum[dest] = seqNum } } diff --git a/deployment/ccip/deploy.go b/deployment/ccip/deploy.go index a0c61cd5f96..4d90422c843 100644 --- a/deployment/ccip/deploy.go +++ b/deployment/ccip/deploy.go @@ -9,17 +9,18 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/smartcontractkit/ccip-owner-contracts/pkg/config" - owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/erc20" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/burn_mint_token_pool" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/nonce_manager" @@ -42,6 +43,7 @@ var ( Router deployment.ContractType = "Router" CommitStore deployment.ContractType = "CommitStore" TokenAdminRegistry deployment.ContractType = "TokenAdminRegistry" + RegistryModule deployment.ContractType = "RegistryModuleOwnerCustom" NonceManager deployment.ContractType = "NonceManager" FeeQuoter deployment.ContractType = "FeeQuoter" AdminManyChainMultisig deployment.ContractType = "AdminManyChainMultiSig" @@ -57,8 +59,10 @@ var ( CapabilitiesRegistry deployment.ContractType = "CapabilitiesRegistry" PriceFeed deployment.ContractType = "PriceFeed" // Note test router maps to a regular router contract. - TestRouter deployment.ContractType = "TestRouter" - CCIPReceiver deployment.ContractType = "CCIPReceiver" + TestRouter deployment.ContractType = "TestRouter" + CCIPReceiver deployment.ContractType = "CCIPReceiver" + BurnMintToken deployment.ContractType = "BurnMintToken" + BurnMintTokenPool deployment.ContractType = "BurnMintTokenPool" ) type Contracts interface { @@ -70,6 +74,7 @@ type Contracts interface { *fee_quoter.FeeQuoter | *router.Router | *token_admin_registry.TokenAdminRegistry | + *registry_module_owner_custom.RegistryModuleOwnerCustom | *weth9.WETH9 | *rmn_remote.RMNRemote | *owner_helpers.ManyChainMultiSig | @@ -77,8 +82,10 @@ type Contracts interface { *offramp.OffRamp | *onramp.OnRamp | *burn_mint_erc677.BurnMintERC677 | + *burn_mint_token_pool.BurnMintTokenPool | *maybe_revert_message_receiver.MaybeRevertMessageReceiver | - *aggregator_v3_interface.AggregatorV3Interface + *aggregator_v3_interface.AggregatorV3Interface | + *erc20.ERC20 } type ContractDeploy[C Contracts] struct { @@ -233,7 +240,6 @@ func DeployCCIPContracts(e deployment.Environment, ab deployment.AddressBook, c if err != nil { return err } - // For each chain, we create a DON on the home chain (2 OCR instances) if err := AddDON( e.Logger, @@ -556,6 +562,29 @@ func DeployChainContracts( } e.Logger.Infow("deployed tokenAdminRegistry", "addr", tokenAdminRegistry) + customRegistryModule, err := deployContract(e.Logger, chain, ab, + func(chain deployment.Chain) ContractDeploy[*registry_module_owner_custom.RegistryModuleOwnerCustom] { + regModAddr, tx2, regMod, err2 := registry_module_owner_custom.DeployRegistryModuleOwnerCustom( + chain.DeployerKey, + chain.Client, + tokenAdminRegistry.Address) + return ContractDeploy[*registry_module_owner_custom.RegistryModuleOwnerCustom]{ + regModAddr, regMod, tx2, deployment.NewTypeAndVersion(RegistryModule, deployment.Version1_5_0), err2, + } + }) + if err != nil { + e.Logger.Errorw("Failed to deploy custom registry module", "err", err) + return err + } + e.Logger.Infow("deployed custom registry module", "addr", tokenAdminRegistry) + + tx, err = tokenAdminRegistry.Contract.AddRegistryModule(chain.DeployerKey, customRegistryModule.Address) + if err != nil { + e.Logger.Errorw("Failed to assign registry module on token admin registry", "err", err) + return err + } + e.Logger.Infow("assigned registry module on token admin registry") + nonceManager, err := deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*nonce_manager.NonceManager] { nonceManagerAddr, tx2, nonceManager, err2 := nonce_manager.DeployNonceManager( diff --git a/deployment/ccip/deploy_home_chain.go b/deployment/ccip/deploy_home_chain.go index 2e306a45fd8..3f614b8510f 100644 --- a/deployment/ccip/deploy_home_chain.go +++ b/deployment/ccip/deploy_home_chain.go @@ -308,12 +308,10 @@ func AddChainConfig( } func BuildOCR3ConfigForCCIPHome( - lggr logger.Logger, ocrSecrets deployment.OCRSecrets, offRamp *offramp.OffRamp, dest deployment.Chain, feedChainSel uint64, - // Token address on Dest chain to aggregate address on feed chain tokenInfo map[ccipocr3.UnknownEncodedAddress]pluginconfig.TokenInfo, nodes deployment.Nodes, rmnHomeAddress common.Address, @@ -360,6 +358,7 @@ func BuildOCR3ConfigForCCIPHome( InflightCacheExpiry: *commonconfig.MustNewDuration(InflightCacheExpiry), RootSnoozeTime: *commonconfig.MustNewDuration(RootSnoozeTime), BatchingStrategyID: BatchingStrategyID, + TokenDataObservers: []pluginconfig.TokenDataObserverConfig{}, }) } if err2 != nil { @@ -891,7 +890,7 @@ func AddDON( home deployment.Chain, nodes deployment.Nodes, ) error { - ocrConfigs, err := BuildOCR3ConfigForCCIPHome(lggr, ocrSecrets, offRamp, dest, feedChainSel, tokenInfo, nodes, rmnHomeAddress) + ocrConfigs, err := BuildOCR3ConfigForCCIPHome(ocrSecrets, offRamp, dest, feedChainSel, tokenInfo, nodes, rmnHomeAddress) if err != nil { return err } diff --git a/deployment/ccip/state.go b/deployment/ccip/state.go index 528d21700cf..650f46d2b3a 100644 --- a/deployment/ccip/state.go +++ b/deployment/ccip/state.go @@ -9,17 +9,17 @@ import ( chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/view" "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_0" + "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_2" + "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_5" + "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_6" common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" - "github.com/smartcontractkit/chainlink/deployment/ccip/view" - "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_2" - "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_5" - "github.com/smartcontractkit/chainlink/deployment/ccip/view/v1_6" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_home" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" @@ -48,6 +48,7 @@ type CCIPChainState struct { RMNProxy *rmn_proxy_contract.RMNProxyContract NonceManager *nonce_manager.NonceManager TokenAdminRegistry *token_admin_registry.TokenAdminRegistry + RegistryModule *registry_module_owner_custom.RegistryModuleOwnerCustom Router *router.Router CommitStore *commit_store.CommitStore Weth9 *weth9.WETH9 @@ -320,6 +321,12 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, err } state.TokenAdminRegistry = tm + case deployment.NewTypeAndVersion(RegistryModule, deployment.Version1_5_0).String(): + rm, err := registry_module_owner_custom.NewRegistryModuleOwnerCustom(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.RegistryModule = rm case deployment.NewTypeAndVersion(Router, deployment.Version1_2_0).String(): r, err := router.NewRouter(common.HexToAddress(address), chain.Client) if err != nil { diff --git a/deployment/ccip/test_assertions.go b/deployment/ccip/test_assertions.go index e6eabaed319..d1389fc5ce3 100644 --- a/deployment/ccip/test_assertions.go +++ b/deployment/ccip/test_assertions.go @@ -63,8 +63,10 @@ func ConfirmGasPriceUpdated( }, []uint64{dest.Selector}) require.NoError(t, err) - require.True(t, it.Next()) - require.NotEqual(t, InitialGasPrice, it.Event.Value) + require.Truef(t, it.Next(), "No gas price update event found on chain %d, fee quoter %s", + dest.Selector, srcFeeQuoter.Address().String()) + require.NotEqualf(t, InitialGasPrice, it.Event.Value, "Gas price not updated on chain %d, fee quoter %s", + dest.Selector, srcFeeQuoter.Address().String()) return nil } diff --git a/deployment/ccip/test_helpers.go b/deployment/ccip/test_helpers.go index 97d311c4240..de1ebd7e675 100644 --- a/deployment/ccip/test_helpers.go +++ b/deployment/ccip/test_helpers.go @@ -3,20 +3,24 @@ package ccipdeployment import ( "context" "fmt" + mapset "github.com/deckarep/golang-set/v2" "math/big" "sort" "testing" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/stretchr/testify/require" - cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "go.uber.org/multierr" "go.uber.org/zap/zapcore" @@ -33,6 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment/environment/devenv" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/burn_mint_token_pool" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_v3_aggregator_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" @@ -228,10 +233,10 @@ func CCIPSendRequest( return tx, blockNum, nil } -func TestSendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, src, dest uint64, testRouter bool) uint64 { +func TestSendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, src, dest uint64, testRouter bool, tokensAndAmounts []router.ClientEVMTokenAmount) uint64 { t.Logf("Sending CCIP request from chain selector %d to chain selector %d", src, dest) - tx, blockNum, err := CCIPSendRequest(e, state, src, dest, []byte("hello"), nil, common.HexToAddress("0x0"), testRouter, nil) + tx, blockNum, err := CCIPSendRequest(e, state, src, dest, []byte("hello"), tokensAndAmounts, common.HexToAddress("0x0"), testRouter, nil) require.NoError(t, err) it, err := state.Chains[src].OnRamp.FilterCCIPMessageSent(&bind.FilterOpts{ Start: blockNum, @@ -372,7 +377,7 @@ func ConfirmRequestOnSourceAndDest(t *testing.T, env deployment.Environment, sta require.NoError(t, err) startBlock := latesthdr.Number.Uint64() fmt.Printf("startblock %d", startBlock) - seqNum := TestSendRequest(t, env, state, sourceCS, destCS, false) + seqNum := TestSendRequest(t, env, state, sourceCS, destCS, false, nil) require.Equal(t, expectedSeqNr, seqNum) fmt.Printf("Request sent for seqnr %d", seqNum) @@ -388,3 +393,251 @@ func ConfirmRequestOnSourceAndDest(t *testing.T, env deployment.Environment, sta return nil } + +func ProcessChangeset(t *testing.T, e deployment.Environment, c deployment.ChangesetOutput) { + + // TODO: Add support for jobspecs as well + + // sign and execute all proposals provided + if len(c.Proposals) != 0 { + state, err := LoadOnchainState(e) + require.NoError(t, err) + for _, prop := range c.Proposals { + chains := mapset.NewSet[uint64]() + for _, op := range prop.Transactions { + chains.Add(uint64(op.ChainIdentifier)) + } + + signed := SignProposal(t, e, &prop) + for _, sel := range chains.ToSlice() { + ExecuteProposal(t, e, signed, state, sel) + } + } + } + + // merge address books + if c.AddressBook != nil { + err := e.ExistingAddresses.Merge(c.AddressBook) + require.NoError(t, err) + } +} + +func DeployTransferableToken( + lggr logger.Logger, + chains map[uint64]deployment.Chain, + src, dst uint64, + state CCIPOnChainState, + addresses deployment.AddressBook, + token string, +) (*burn_mint_erc677.BurnMintERC677, *burn_mint_token_pool.BurnMintTokenPool, *burn_mint_erc677.BurnMintERC677, *burn_mint_token_pool.BurnMintTokenPool, error) { + // Deploy token and pools + srcToken, srcPool, err := deployTransferTokenOneEnd(lggr, chains[src], addresses, token) + if err != nil { + return nil, nil, nil, nil, err + } + dstToken, dstPool, err := deployTransferTokenOneEnd(lggr, chains[dst], addresses, token) + if err != nil { + return nil, nil, nil, nil, err + } + + // Attach token pools to registry + if err := attachTokenToTheRegistry(chains[src], state.Chains[src], chains[src].DeployerKey, srcToken.Address(), srcPool.Address()); err != nil { + return nil, nil, nil, nil, err + } + + if err := attachTokenToTheRegistry(chains[dst], state.Chains[dst], chains[dst].DeployerKey, dstToken.Address(), dstPool.Address()); err != nil { + return nil, nil, nil, nil, err + } + + // Connect pool to each other + if err := setTokenPoolCounterPart(chains[src], srcPool, dst, dstToken.Address(), dstPool.Address()); err != nil { + return nil, nil, nil, nil, err + } + + if err := setTokenPoolCounterPart(chains[dst], dstPool, src, srcToken.Address(), srcPool.Address()); err != nil { + return nil, nil, nil, nil, err + } + + // Add burn/mint permissions + if err := grantMintBurnPermissions(chains[src], srcToken, srcPool.Address()); err != nil { + return nil, nil, nil, nil, err + } + + if err := grantMintBurnPermissions(chains[dst], dstToken, dstPool.Address()); err != nil { + return nil, nil, nil, nil, err + } + + return srcToken, srcPool, dstToken, dstPool, nil +} + +func grantMintBurnPermissions(chain deployment.Chain, token *burn_mint_erc677.BurnMintERC677, address common.Address) error { + tx, err := token.GrantBurnRole(chain.DeployerKey, address) + if err != nil { + return err + } + _, err = chain.Confirm(tx) + if err != nil { + return err + } + + tx, err = token.GrantMintRole(chain.DeployerKey, address) + if err != nil { + return err + } + _, err = chain.Confirm(tx) + return err +} + +func setTokenPoolCounterPart( + chain deployment.Chain, + tokenPool *burn_mint_token_pool.BurnMintTokenPool, + destChainSelector uint64, + destTokenAddress common.Address, + destTokenPoolAddress common.Address, +) error { + tx, err := tokenPool.ApplyChainUpdates( + chain.DeployerKey, + []burn_mint_token_pool.TokenPoolChainUpdate{ + { + RemoteChainSelector: destChainSelector, + Allowed: true, + RemotePoolAddress: common.LeftPadBytes(destTokenPoolAddress.Bytes(), 32), + RemoteTokenAddress: common.LeftPadBytes(destTokenAddress.Bytes(), 32), + OutboundRateLimiterConfig: burn_mint_token_pool.RateLimiterConfig{ + IsEnabled: false, + Capacity: big.NewInt(0), + Rate: big.NewInt(0), + }, + InboundRateLimiterConfig: burn_mint_token_pool.RateLimiterConfig{ + IsEnabled: false, + Capacity: big.NewInt(0), + Rate: big.NewInt(0), + }, + }, + }, + ) + if err != nil { + return err + } + + _, err = chain.Confirm(tx) + if err != nil { + return err + } + + tx, err = tokenPool.SetRemotePool( + chain.DeployerKey, + destChainSelector, + destTokenPoolAddress.Bytes(), + ) + return err +} + +func attachTokenToTheRegistry( + chain deployment.Chain, + state CCIPChainState, + owner *bind.TransactOpts, + token common.Address, + tokenPool common.Address, +) error { + tx, err := state.RegistryModule.RegisterAdminViaOwner(owner, token) + if err != nil { + return err + } + _, err = chain.Confirm(tx) + if err != nil { + return err + } + + tx, err = state.TokenAdminRegistry.AcceptAdminRole(owner, token) + if err != nil { + return err + } + _, err = chain.Confirm(tx) + if err != nil { + return err + } + + tx, err = state.TokenAdminRegistry.SetPool(owner, token, tokenPool) + if err != nil { + return err + } + _, err = chain.Confirm(tx) + if err != nil { + return err + } + return nil +} + +func deployTransferTokenOneEnd( + lggr logger.Logger, + chain deployment.Chain, + addressBook deployment.AddressBook, + tokenSymbol string, +) (*burn_mint_erc677.BurnMintERC677, *burn_mint_token_pool.BurnMintTokenPool, error) { + var rmnAddress, routerAddress string + chainAddresses, err := addressBook.AddressesForChain(chain.Selector) + if err != nil { + return nil, nil, err + } + for address, v := range chainAddresses { + if deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_0_0) == v { + rmnAddress = address + } + if deployment.NewTypeAndVersion(Router, deployment.Version1_2_0) == v { + routerAddress = address + } + if rmnAddress != "" && routerAddress != "" { + break + } + } + + tokenContract, err := deployContract(lggr, chain, addressBook, + func(chain deployment.Chain) ContractDeploy[*burn_mint_erc677.BurnMintERC677] { + USDCTokenAddr, tx, token, err2 := burn_mint_erc677.DeployBurnMintERC677( + chain.DeployerKey, + chain.Client, + tokenSymbol, + tokenSymbol, + uint8(18), + big.NewInt(0).Mul(big.NewInt(1e9), big.NewInt(1e18)), + ) + return ContractDeploy[*burn_mint_erc677.BurnMintERC677]{ + USDCTokenAddr, token, tx, deployment.NewTypeAndVersion(BurnMintToken, deployment.Version1_0_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy Token ERC677", "err", err) + return nil, nil, err + } + + tx, err := tokenContract.Contract.GrantMintRole(chain.DeployerKey, chain.DeployerKey.From) + if err != nil { + return nil, nil, err + } + _, err = chain.Confirm(tx) + if err != nil { + return nil, nil, err + } + + tokenPool, err := deployContract(lggr, chain, addressBook, + func(chain deployment.Chain) ContractDeploy[*burn_mint_token_pool.BurnMintTokenPool] { + tokenPoolAddress, tx, tokenPoolContract, err2 := burn_mint_token_pool.DeployBurnMintTokenPool( + chain.DeployerKey, + chain.Client, + tokenContract.Address, + []common.Address{}, + common.HexToAddress(rmnAddress), + common.HexToAddress(routerAddress), + ) + return ContractDeploy[*burn_mint_token_pool.BurnMintTokenPool]{ + tokenPoolAddress, tokenPoolContract, tx, deployment.NewTypeAndVersion(BurnMintTokenPool, deployment.Version1_0_0), err2, + } + }) + if err != nil { + lggr.Errorw("Failed to deploy token pool", "err", err) + return nil, nil, err + } + + return tokenContract.Contract, tokenPool.Contract, nil +} diff --git a/deployment/environment.go b/deployment/environment.go index a975042dee2..104db4c5c37 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -18,12 +18,11 @@ import ( types3 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "google.golang.org/grpc" + "github.com/smartcontractkit/chainlink-common/pkg/logger" csav1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/csa" jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" ) diff --git a/deployment/go.mod b/deployment/go.mod index d69f04fe55d..c453481d9a2 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -23,7 +23,7 @@ require ( github.com/sethvargo/go-retry v0.2.4 github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 github.com/smartcontractkit/chain-selectors v1.0.27 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 @@ -398,7 +398,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect - github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 // indirect + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 // indirect github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.5 // indirect diff --git a/deployment/go.sum b/deployment/go.sum index 0607d6161fd..2716f7a9668 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1382,8 +1382,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= @@ -1396,8 +1396,8 @@ github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 h1:1xTm8UGeD github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 h1:ZUihu/AMiFkZgO5XkVcpFayhIUibdovHzpbHnMPZUr0= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 h1:YsE0uS6S10oAWnFbjNDc7tN9JrWYjvyqMnTSbTSgl00= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 h1:B4DFdk6MGcQnoCjjMBCx7Z+GWQpxRWJ4O8W/dVJyWGA= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8/go.mod h1:WkBqgBo+g34Gm5vWkDDl8Fh3Mzd7bF5hXp7rryg0t5o= github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.13 h1:T0kbw07Vb6xUyA9MIJZfErMgWseWi1zf7cYvRpoq7ug= diff --git a/deployment/keystone/changeset/append_node_capbilities.go b/deployment/keystone/changeset/append_node_capbilities.go index 20988825110..0cee9b442c8 100644 --- a/deployment/keystone/changeset/append_node_capbilities.go +++ b/deployment/keystone/changeset/append_node_capbilities.go @@ -3,10 +3,6 @@ package changeset import ( "fmt" - chainsel "github.com/smartcontractkit/chain-selectors" - kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" - "github.com/smartcontractkit/chainlink/deployment" kslib "github.com/smartcontractkit/chainlink/deployment/keystone" "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" @@ -14,45 +10,10 @@ import ( var _ deployment.ChangeSet = AppendNodeCapabilities -type AppendNodeCapabilitiesRequest struct { - AddressBook deployment.AddressBook - RegistryChainSel uint64 - - P2pToCapabilities map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability - NopToNodes map[kcr.CapabilitiesRegistryNodeOperator][]*P2PSignerEnc -} - -func (req *AppendNodeCapabilitiesRequest) Validate() error { - if len(req.P2pToCapabilities) == 0 { - return fmt.Errorf("p2pToCapabilities is empty") - } - if len(req.NopToNodes) == 0 { - return fmt.Errorf("nopToNodes is empty") - } - if req.AddressBook == nil { - return fmt.Errorf("registry is nil") - } - _, exists := chainsel.ChainBySelector(req.RegistryChainSel) - if !exists { - return fmt.Errorf("registry chain selector %d does not exist", req.RegistryChainSel) - } - - return nil -} - -/* -// AppendNodeCapabilibity adds any new capabilities to the registry, merges the new capabilities with the existing capabilities -// of the node, and updates the nodes in the registry host the union of the new and existing capabilities. -func AppendNodeCapabilities(lggr logger.Logger, req *AppendNodeCapabilitiesRequest) (deployment.ChangesetOutput, error) { - _, err := appendNodeCapabilitiesImpl(lggr, req) - if err != nil { - return deployment.ChangesetOutput{}, err - } - return deployment.ChangesetOutput{}, nil -} -*/ +// AppendNodeCapabilitiesRequest is a request to add capabilities to the existing capabilities of nodes in the registry +type AppendNodeCapabilitiesRequest = MutateNodeCapabilitiesRequest -// AppendNodeCapabilibity adds any new capabilities to the registry, merges the new capabilities with the existing capabilities +// AppendNodeCapabilities adds any new capabilities to the registry, merges the new capabilities with the existing capabilities // of the node, and updates the nodes in the registry host the union of the new and existing capabilities. func AppendNodeCapabilities(env deployment.Environment, config any) (deployment.ChangesetOutput, error) { req, ok := config.(*AppendNodeCapabilitiesRequest) @@ -79,7 +40,7 @@ func (req *AppendNodeCapabilitiesRequest) convert(e deployment.Environment) (*in if !ok { return nil, fmt.Errorf("registry chain selector %d does not exist in environment", req.RegistryChainSel) } - contracts, err := kslib.GetContractSets(&kslib.GetContractSetsRequest{ + contracts, err := kslib.GetContractSets(e.Logger, &kslib.GetContractSetsRequest{ Chains: map[uint64]deployment.Chain{req.RegistryChainSel: registryChain}, AddressBook: req.AddressBook, }) diff --git a/deployment/keystone/changeset/update_node_capabilities.go b/deployment/keystone/changeset/update_node_capabilities.go index 462a527273d..49ee2cb394c 100644 --- a/deployment/keystone/changeset/update_node_capabilities.go +++ b/deployment/keystone/changeset/update_node_capabilities.go @@ -6,6 +6,7 @@ import ( chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/keystone" kslib "github.com/smartcontractkit/chainlink/deployment/keystone" "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" @@ -17,7 +18,23 @@ var _ deployment.ChangeSet = UpdateNodeCapabilities type P2PSignerEnc = internal.P2PSignerEnc -type UpdateNodeCapabilitiesRequest struct { +func NewP2PSignerEnc(n *keystone.Node, registryChainSel uint64) (*P2PSignerEnc, error) { + p2p, signer, enc, err := kslib.ExtractKeys(n, registryChainSel) + if err != nil { + return nil, fmt.Errorf("failed to extract keys: %w", err) + } + return &P2PSignerEnc{ + Signer: signer, + P2PKey: p2p, + EncryptionPublicKey: enc, + }, nil +} + +// UpdateNodeCapabilitiesRequest is a request to set the capabilities of nodes in the registry +type UpdateNodeCapabilitiesRequest = MutateNodeCapabilitiesRequest + +// MutateNodeCapabilitiesRequest is a request to change the capabilities of nodes in the registry +type MutateNodeCapabilitiesRequest struct { AddressBook deployment.AddressBook RegistryChainSel uint64 @@ -25,7 +42,7 @@ type UpdateNodeCapabilitiesRequest struct { NopToNodes map[kcr.CapabilitiesRegistryNodeOperator][]*P2PSignerEnc } -func (req *UpdateNodeCapabilitiesRequest) Validate() error { +func (req *MutateNodeCapabilitiesRequest) Validate() error { if req.AddressBook == nil { return fmt.Errorf("address book is nil") } @@ -39,32 +56,11 @@ func (req *UpdateNodeCapabilitiesRequest) Validate() error { if !exists { return fmt.Errorf("registry chain selector %d does not exist", req.RegistryChainSel) } - return nil -} - -type UpdateNodeCapabilitiesImplRequest struct { - Chain deployment.Chain - Registry *kcr.CapabilitiesRegistry - - P2pToCapabilities map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability - NopToNodes map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc -} - -func (req *UpdateNodeCapabilitiesImplRequest) Validate() error { - if len(req.P2pToCapabilities) == 0 { - return fmt.Errorf("p2pToCapabilities is empty") - } - if len(req.NopToNodes) == 0 { - return fmt.Errorf("nopToNodes is empty") - } - if req.Registry == nil { - return fmt.Errorf("registry is nil") - } return nil } -func (req *UpdateNodeCapabilitiesRequest) updateNodeCapabilitiesImplRequest(e deployment.Environment) (*internal.UpdateNodeCapabilitiesImplRequest, error) { +func (req *MutateNodeCapabilitiesRequest) updateNodeCapabilitiesImplRequest(e deployment.Environment) (*internal.UpdateNodeCapabilitiesImplRequest, error) { if err := req.Validate(); err != nil { return nil, fmt.Errorf("failed to validate UpdateNodeCapabilitiesRequest: %w", err) } @@ -72,7 +68,7 @@ func (req *UpdateNodeCapabilitiesRequest) updateNodeCapabilitiesImplRequest(e de if !ok { return nil, fmt.Errorf("registry chain selector %d does not exist in environment", req.RegistryChainSel) } - contracts, err := kslib.GetContractSets(&kslib.GetContractSetsRequest{ + contracts, err := kslib.GetContractSets(e.Logger, &kslib.GetContractSetsRequest{ Chains: map[uint64]deployment.Chain{req.RegistryChainSel: registryChain}, AddressBook: req.AddressBook, }) @@ -83,6 +79,7 @@ func (req *UpdateNodeCapabilitiesRequest) updateNodeCapabilitiesImplRequest(e de if registry == nil { return nil, fmt.Errorf("capabilities registry not found for chain %d", req.RegistryChainSel) } + return &internal.UpdateNodeCapabilitiesImplRequest{ Chain: registryChain, Registry: registry, @@ -93,9 +90,9 @@ func (req *UpdateNodeCapabilitiesRequest) updateNodeCapabilitiesImplRequest(e de // UpdateNodeCapabilities updates the capabilities of nodes in the registry func UpdateNodeCapabilities(env deployment.Environment, config any) (deployment.ChangesetOutput, error) { - req, ok := config.(*UpdateNodeCapabilitiesRequest) + req, ok := config.(*MutateNodeCapabilitiesRequest) if !ok { - return deployment.ChangesetOutput{}, fmt.Errorf("invalid config type. want %T, got %T", &UpdateNodeCapabilitiesRequest{}, config) + return deployment.ChangesetOutput{}, fmt.Errorf("invalid config type. want %T, got %T", &MutateNodeCapabilitiesRequest{}, config) } c, err := req.updateNodeCapabilitiesImplRequest(env) if err != nil { diff --git a/deployment/keystone/changeset/view.go b/deployment/keystone/changeset/view.go index 3f8d3c256f5..cab4ca25ae7 100644 --- a/deployment/keystone/changeset/view.go +++ b/deployment/keystone/changeset/view.go @@ -14,7 +14,7 @@ import ( var _ deployment.ViewState = ViewKeystone func ViewKeystone(e deployment.Environment) (json.Marshaler, error) { - state, err := keystone.GetContractSets(&keystone.GetContractSetsRequest{ + state, err := keystone.GetContractSets(e.Logger, &keystone.GetContractSetsRequest{ Chains: e.Chains, AddressBook: e.ExistingAddresses, }) diff --git a/deployment/keystone/deploy.go b/deployment/keystone/deploy.go index 2bc14c6e1e8..1e6bc0ecf82 100644 --- a/deployment/keystone/deploy.go +++ b/deployment/keystone/deploy.go @@ -232,7 +232,7 @@ func ConfigureRegistry(ctx context.Context, lggr logger.Logger, req ConfigureCon return nil, fmt.Errorf("chain %d not found in environment", req.RegistryChainSel) } - contractSetsResp, err := GetContractSets(&GetContractSetsRequest{ + contractSetsResp, err := GetContractSets(req.Env.Logger, &GetContractSetsRequest{ Chains: req.Env.Chains, AddressBook: addrBook, }) @@ -336,7 +336,7 @@ func ConfigureRegistry(ctx context.Context, lggr logger.Logger, req ConfigureCon // ConfigureForwardContracts configures the forwarder contracts on all chains for the given DONS // the address book is required to contain the an address of the deployed forwarder contract for every chain in the environment func ConfigureForwardContracts(env *deployment.Environment, dons []RegisteredDon, addrBook deployment.AddressBook) error { - contractSetsResp, err := GetContractSets(&GetContractSetsRequest{ + contractSetsResp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, AddressBook: addrBook, }) @@ -371,7 +371,7 @@ func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons [] return fmt.Errorf("chain %d not found in environment", chainSel) } - contractSetsResp, err := GetContractSets(&GetContractSetsRequest{ + contractSetsResp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, AddressBook: addrBook, }) @@ -411,7 +411,7 @@ func ConfigureOCR3ContractFromJD(env *deployment.Environment, chainSel uint64, n if !ok { return fmt.Errorf("chain %d not found in environment", chainSel) } - contractSetsResp, err := GetContractSets(&GetContractSetsRequest{ + contractSetsResp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, AddressBook: addrBook, }) diff --git a/deployment/keystone/deploy_test.go b/deployment/keystone/deploy_test.go index 7be38a56528..e6474340a59 100644 --- a/deployment/keystone/deploy_test.go +++ b/deployment/keystone/deploy_test.go @@ -332,7 +332,7 @@ func TestDeployCLO(t *testing.T) { AddressBook: ad, } - contractSetsResp, err := keystone.GetContractSets(req) + contractSetsResp, err := keystone.GetContractSets(lggr, req) require.NoError(t, err) require.Len(t, contractSetsResp.ContractSets, len(env.Chains)) // check the registry diff --git a/deployment/keystone/state.go b/deployment/keystone/state.go index e226d3b4b91..33200a40e02 100644 --- a/deployment/keystone/state.go +++ b/deployment/keystone/state.go @@ -5,6 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/deployment" common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" "github.com/smartcontractkit/chainlink/deployment/keystone/view" @@ -40,7 +41,7 @@ func (cs ContractSet) View() (view.KeystoneChainView, error) { return out, nil } -func GetContractSets(req *GetContractSetsRequest) (*GetContractSetsResponse, error) { +func GetContractSets(lggr logger.Logger, req *GetContractSetsRequest) (*GetContractSetsResponse, error) { resp := &GetContractSetsResponse{ ContractSets: make(map[uint64]ContractSet), } @@ -49,7 +50,7 @@ func GetContractSets(req *GetContractSetsRequest) (*GetContractSetsResponse, err if err != nil { return nil, fmt.Errorf("failed to get addresses for chain %d: %w", id, err) } - cs, err := loadContractSet(chain, addrs) + cs, err := loadContractSet(lggr, chain, addrs) if err != nil { return nil, fmt.Errorf("failed to load contract set for chain %d: %w", id, err) } @@ -58,7 +59,7 @@ func GetContractSets(req *GetContractSetsRequest) (*GetContractSetsResponse, err return resp, nil } -func loadContractSet(chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*ContractSet, error) { +func loadContractSet(lggr logger.Logger, chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*ContractSet, error) { var out ContractSet for addr, tv := range addresses { @@ -83,7 +84,8 @@ func loadContractSet(chain deployment.Chain, addresses map[string]deployment.Typ } out.OCR3 = c default: - return nil, fmt.Errorf("unknown contract type %s", tv.Type) + lggr.Warnw("unknown contract type", "type", tv.Type) + // ignore unknown contract types } } return &out, nil diff --git a/deployment/keystone/types.go b/deployment/keystone/types.go index 7190d9ea8b8..b0ed43a31ad 100644 --- a/deployment/keystone/types.go +++ b/deployment/keystone/types.go @@ -122,6 +122,14 @@ func newOcr2NodeFromClo(n *Node, registryChainSel uint64) (*ocr2Node, error) { return newOcr2Node(n.P2PID, cfgs, *n.PublicKey) } +func ExtractKeys(n *Node, registerChainSel uint64) (p2p p2pkey.PeerID, signer [32]byte, encPubKey [32]byte, err error) { + orc2n, err := newOcr2NodeFromClo(n, registerChainSel) + if err != nil { + return p2p, signer, encPubKey, fmt.Errorf("failed to create ocr2 node for node %s: %w", n.ID, err) + } + return orc2n.P2PKey, orc2n.Signer, orc2n.EncryptionPublicKey, nil +} + func newOcr2Node(id string, ccfgs map[chaintype.ChainType]*v1.ChainConfig, csaPubKey string) (*ocr2Node, error) { if ccfgs == nil { return nil, errors.New("nil ocr2config") @@ -201,31 +209,40 @@ type DonCapabilities struct { // map the node id to the NOP func (dc DonInfo) nodeIdToNop(cs uint64) (map[string]capabilities_registry.CapabilitiesRegistryNodeOperator, error) { - cid, err := chainsel.ChainIdFromSelector(cs) - if err != nil { - return nil, fmt.Errorf("failed to get chain id from selector %d: %w", cs, err) - } - cidStr := strconv.FormatUint(cid, 10) out := make(map[string]capabilities_registry.CapabilitiesRegistryNodeOperator) for _, node := range dc.Nodes { - found := false - for _, chain := range node.ChainConfigs { - //TODO validate chainType field - if chain.Chain.Id == cidStr { - found = true - out[node.P2PID] = capabilities_registry.CapabilitiesRegistryNodeOperator{ - Name: node.Name, - Admin: adminAddr(chain.AdminAddress), - } - } - } - if !found { - return nil, fmt.Errorf("node '%s' %s does not support chain %d", node.Name, node.ID, cid) + a, err := AdminAddress(&node, cs) + if err != nil { + return nil, fmt.Errorf("failed to get admin address for node %s: %w", node.ID, err) } + out[node.ID] = NodeOperator(dc.Name, a) + } return out, nil } +func NodeOperator(name string, adminAddress string) capabilities_registry.CapabilitiesRegistryNodeOperator { + return capabilities_registry.CapabilitiesRegistryNodeOperator{ + Name: name, + Admin: adminAddr(adminAddress), + } +} + +func AdminAddress(n *Node, chainSel uint64) (string, error) { + cid, err := chainsel.ChainIdFromSelector(chainSel) + if err != nil { + return "", fmt.Errorf("failed to get chain id from selector %d: %w", chainSel, err) + } + cidStr := strconv.FormatUint(cid, 10) + for _, chain := range n.ChainConfigs { + //TODO validate chainType field + if chain.Chain.Id == cidStr { + return chain.AdminAddress, nil + } + } + return "", fmt.Errorf("no chain config for chain %d", cid) +} + // helpers to maintain compatibility with the existing registration functions // nodesToNops converts a list of DonCapabilities to a map of node id to NOP func nodesToNops(dons []DonInfo, chainSel uint64) (map[string]capabilities_registry.CapabilitiesRegistryNodeOperator, error) { diff --git a/docs/CONFIG.md b/docs/CONFIG.md index a1f583ec2d7..8e872a458e6 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -9503,8 +9503,8 @@ HistoryDepth = 100 # Default ``` HistoryDepth tracks the top N blocks on top of the latest finalized block to keep in the `heads` database table. Note that this can easily result in MORE than `N + finality depth` records since in the case of re-orgs we keep multiple heads for a particular block height. -This number should be at least as large as `FinalityDepth`. -There may be a small performance penalty to setting this to something very large (10,000+) +Higher values help reduce number of RPC requests performed by TXM's Finalizer and improve TXM's Confirmer reorg protection on restarts. +At the same time, setting the value too high could lead to higher CPU consumption. The following formula could be used to calculate the optimal value: `expected_downtime_on_restart/block_time`. ### MaxBufferSize ```toml @@ -10064,6 +10064,7 @@ ComputeUnitPriceMin = 0 # Default ComputeUnitPriceDefault = 0 # Default FeeBumpPeriod = '3s' # Default BlockHistoryPollPeriod = '5s' # Default +BlockHistorySize = 1 # Default ComputeUnitLimitDefault = 200_000 # Default EstimateComputeUnitLimit = false # Default ``` @@ -10178,6 +10179,15 @@ BlockHistoryPollPeriod = '5s' # Default ``` BlockHistoryPollPeriod is the rate to poll for blocks in the block history fee estimator +### BlockHistorySize +```toml +BlockHistorySize = 1 # Default +``` +BlockHistorySize is the number of blocks to take into consideration when using FeeEstimatorMode = 'blockhistory' to determine compute unit price. +If set to 1, the compute unit price will be determined by the median of the last block's compute unit prices. +If set N > 1, the compute unit price will be determined by the average of the medians of the last N blocks' compute unit prices. +DISCLAIMER: 1:1 ratio between n and RPC calls. It executes once every 'BlockHistoryPollPeriod' value. + ### ComputeUnitLimitDefault ```toml ComputeUnitLimitDefault = 200_000 # Default diff --git a/go.mod b/go.mod index cd089a62e6d..b7a89eddf8d 100644 --- a/go.mod +++ b/go.mod @@ -75,13 +75,13 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e github.com/smartcontractkit/chainlink-feeds v0.1.1 github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 - github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de diff --git a/go.sum b/go.sum index fceb95c5efb..e2d7559562b 100644 --- a/go.sum +++ b/go.sum @@ -1075,8 +1075,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= @@ -1087,8 +1087,8 @@ github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6An github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 h1:ZUihu/AMiFkZgO5XkVcpFayhIUibdovHzpbHnMPZUr0= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 h1:YsE0uS6S10oAWnFbjNDc7tN9JrWYjvyqMnTSbTSgl00= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 h1:B4DFdk6MGcQnoCjjMBCx7Z+GWQpxRWJ4O8W/dVJyWGA= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8/go.mod h1:WkBqgBo+g34Gm5vWkDDl8Fh3Mzd7bF5hXp7rryg0t5o= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= diff --git a/integration-tests/ccip-tests/actions/ccip_helpers.go b/integration-tests/ccip-tests/actions/ccip_helpers.go index a9a873f23cd..c24ae2ecd54 100644 --- a/integration-tests/ccip-tests/actions/ccip_helpers.go +++ b/integration-tests/ccip-tests/actions/ccip_helpers.go @@ -3072,7 +3072,7 @@ func (lane *CCIPLane) ExecuteManually(options ...ManualExecutionOption) error { GasLimit: big.NewInt(DefaultDestinationGasLimit), } timeNow := time.Now().UTC() - tx, err := args.ExecuteManually() + tx, err := args.ExecuteManually(lane.Context) if err != nil { return fmt.Errorf("could not execute manually: %w seqNum %d", err, seqNum) } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index b223f729e8b..518dece2509 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -35,7 +35,7 @@ require ( github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.27 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 @@ -412,7 +412,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect - github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 // indirect + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index df4ad771076..736bcd8bfc2 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1403,8 +1403,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= @@ -1417,8 +1417,8 @@ github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0 h1:1xTm8UGeD github.com/smartcontractkit/chainlink-protos/job-distributor v0.4.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 h1:ZUihu/AMiFkZgO5XkVcpFayhIUibdovHzpbHnMPZUr0= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 h1:YsE0uS6S10oAWnFbjNDc7tN9JrWYjvyqMnTSbTSgl00= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 h1:B4DFdk6MGcQnoCjjMBCx7Z+GWQpxRWJ4O8W/dVJyWGA= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8/go.mod h1:WkBqgBo+g34Gm5vWkDDl8Fh3Mzd7bF5hXp7rryg0t5o= github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 h1:GDGrC5OGiV0RyM1znYWehSQXyZQWTOzrEeJRYmysPCE= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 2cbbda250fd..b2e85876e35 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -64,7 +64,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 // indirect github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f // indirect github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect @@ -418,7 +418,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect - github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 // indirect + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 // indirect github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 // indirect github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index f6c81e4c500..8f18bccf4e1 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1392,8 +1392,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3 github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4 h1:GWjim4uGGFbye4XbJP0cPAbARhc8u3cAJU8jLYy0mXM= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20241104130643-4b7e196370c4/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7 h1:AGi0kAtMRW1zl1h7sGw+3CKO4Nlev6iA08YfEcgJCGs= github.com/smartcontractkit/chainlink-common v0.3.1-0.20241101093830-33711d0c3de7/go.mod h1:TQ9/KKXZ9vr8QAlUquqGpSvDCpR+DtABKPXZY4CiRns= github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw= @@ -1404,8 +1404,8 @@ github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6An github.com/smartcontractkit/chainlink-feeds v0.1.1/go.mod h1:55EZ94HlKCfAsUiKUTNI7QlE/3d3IwTlsU3YNa/nBb4= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 h1:PBUaFfPLm+Efq7H9kdfGBivH+QhJ6vB5EZTR/sCZsxI= github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0/go.mod h1:m/A3lqD7ms/RsQ9BT5P2uceYY0QX5mIt4KQxT2G6qEo= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 h1:ZUihu/AMiFkZgO5XkVcpFayhIUibdovHzpbHnMPZUr0= -github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6 h1:YsE0uS6S10oAWnFbjNDc7tN9JrWYjvyqMnTSbTSgl00= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241104202120-39cabce465f6/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 h1:B4DFdk6MGcQnoCjjMBCx7Z+GWQpxRWJ4O8W/dVJyWGA= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8/go.mod h1:WkBqgBo+g34Gm5vWkDDl8Fh3Mzd7bF5hXp7rryg0t5o= github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 h1:GDGrC5OGiV0RyM1znYWehSQXyZQWTOzrEeJRYmysPCE= diff --git a/integration-tests/smoke/ccip_rmn_test.go b/integration-tests/smoke/ccip_rmn_test.go index 53bf25a3ac5..76f69afd99e 100644 --- a/integration-tests/smoke/ccip_rmn_test.go +++ b/integration-tests/smoke/ccip_rmn_test.go @@ -184,7 +184,7 @@ func TestRMN(t *testing.T) { require.NoError(t, err) block := latesthdr.Number.Uint64() startBlocks[dest] = &block - seqNum := ccipdeployment.TestSendRequest(t, e, onChainState, src, dest, false) + seqNum := ccipdeployment.TestSendRequest(t, e, onChainState, src, dest, false, nil) expectedSeqNum[dest] = seqNum } } diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index 8d429b5a772..686f2c10299 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -1,23 +1,22 @@ package smoke import ( + "math/big" "testing" "github.com/stretchr/testify/require" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-ccip/pluginconfig" - + jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/chainlink/deployment" ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/logger" - - jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" ) func TestInitialDeployOnLocal(t *testing.T) { @@ -84,7 +83,7 @@ func TestInitialDeployOnLocal(t *testing.T) { require.NoError(t, err) block := latesthdr.Number.Uint64() startBlocks[dest] = &block - seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false) + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) expectedSeqNum[dest] = seqNum } } @@ -106,3 +105,150 @@ func TestInitialDeployOnLocal(t *testing.T) { // TODO: Apply the proposal. } + +func TestTokenTransfer(t *testing.T) { + lggr := logger.TestLogger(t) + ctx := ccdeploy.Context(t) + tenv, _, _ := testsetups.NewLocalDevEnvironment(t, lggr) + + e := tenv.Env + state, err := ccdeploy.LoadOnchainState(e) + require.NoError(t, err) + + feeds := state.Chains[tenv.FeedChainSel].USDFeeds + tokenConfig := ccdeploy.NewTokenConfig() + tokenConfig.UpsertTokenInfo(ccdeploy.LinkSymbol, + pluginconfig.TokenInfo{ + AggregatorAddress: cciptypes.UnknownEncodedAddress(feeds[ccdeploy.LinkSymbol].Address().String()), + Decimals: ccdeploy.LinkDecimals, + DeviationPPB: cciptypes.NewBigIntFromInt64(1e9), + }, + ) + + // Apply migration + output, err := changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ + HomeChainSel: tenv.HomeChainSel, + FeedChainSel: tenv.FeedChainSel, + ChainsToDeploy: e.AllChainSelectors(), + TokenConfig: tokenConfig, + MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + }) + require.NoError(t, err) + require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) + // Get new state after migration and mock USDC token deployment. + state, err = ccdeploy.LoadOnchainState(e) + require.NoError(t, err) + + srcToken, _, dstToken, _, err := ccdeploy.DeployTransferableToken( + lggr, + tenv.Env.Chains, + tenv.HomeChainSel, + tenv.FeedChainSel, + state, + e.ExistingAddresses, + "MY_TOKEN", + ) + require.NoError(t, err) + + // Ensure capreg logs are up to date. + ccdeploy.ReplayLogs(t, e.Offchain, tenv.ReplayBlocks) + + // Apply the jobs. + for nodeID, jobs := range output.JobSpecs { + for _, job := range jobs { + // Note these auto-accept + _, err := e.Offchain.ProposeJob(ctx, + &jobv1.ProposeJobRequest{ + NodeId: nodeID, + Spec: job, + }) + require.NoError(t, err) + } + } + + // Add all lanes + require.NoError(t, ccdeploy.AddLanesForAll(e, state)) + // Need to keep track of the block number for each chain so that event subscription can be done from that block. + startBlocks := make(map[uint64]*uint64) + // Send a message from each chain to every other chain. + expectedSeqNum := make(map[uint64]uint64) + + twoCoins := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(2)) + tx, err := srcToken.Mint( + e.Chains[tenv.HomeChainSel].DeployerKey, + e.Chains[tenv.HomeChainSel].DeployerKey.From, + new(big.Int).Mul(twoCoins, big.NewInt(10)), + ) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + tx, err = dstToken.Mint( + e.Chains[tenv.FeedChainSel].DeployerKey, + e.Chains[tenv.FeedChainSel].DeployerKey.From, + new(big.Int).Mul(twoCoins, big.NewInt(10)), + ) + require.NoError(t, err) + _, err = e.Chains[tenv.FeedChainSel].Confirm(tx) + require.NoError(t, err) + + tx, err = srcToken.Approve(e.Chains[tenv.HomeChainSel].DeployerKey, state.Chains[tenv.HomeChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + tx, err = dstToken.Approve(e.Chains[tenv.FeedChainSel].DeployerKey, state.Chains[tenv.FeedChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.FeedChainSel].Confirm(tx) + require.NoError(t, err) + + tokens := map[uint64][]router.ClientEVMTokenAmount{ + tenv.HomeChainSel: {{ + Token: srcToken.Address(), + Amount: twoCoins, + }}, + tenv.FeedChainSel: {{ + Token: dstToken.Address(), + Amount: twoCoins, + }}, + } + + for src := range e.Chains { + for dest, destChain := range e.Chains { + if src == dest { + continue + } + latesthdr, err := destChain.Client.HeaderByNumber(testcontext.Get(t), nil) + require.NoError(t, err) + block := latesthdr.Number.Uint64() + startBlocks[dest] = &block + + if src == tenv.HomeChainSel && dest == tenv.FeedChainSel { + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, tokens[src]) + expectedSeqNum[dest] = seqNum + } else { + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil) + expectedSeqNum[dest] = seqNum + } + } + } + + // Wait for all commit reports to land. + ccdeploy.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks) + + // After commit is reported on all chains, token prices should be updated in FeeQuoter. + for dest := range e.Chains { + linkAddress := state.Chains[dest].LinkToken.Address() + feeQuoter := state.Chains[dest].FeeQuoter + timestampedPrice, err := feeQuoter.GetTokenPrice(nil, linkAddress) + require.NoError(t, err) + require.Equal(t, ccdeploy.MockLinkPrice, timestampedPrice.Value) + } + + // Wait for all exec reports to land + ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks) + + balance, err := dstToken.BalanceOf(nil, state.Chains[tenv.FeedChainSel].Receiver.Address()) + require.NoError(t, err) + require.Equal(t, twoCoins, balance) +} diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index 284c3c40c65..69da49ae404 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -11,6 +11,7 @@ import ( "os/signal" "sort" "strings" + "sync" "syscall" "testing" "time" @@ -20,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/pelletier/go-toml/v2" "github.com/rs/zerolog" "github.com/stretchr/testify/require" @@ -615,12 +615,19 @@ func (o *OCRSoakTest) testLoop(testDuration time.Duration, newValue int) { interruption := make(chan os.Signal, 1) //nolint:staticcheck //ignore SA1016 we need to send the os.Kill signal signal.Notify(interruption, os.Kill, os.Interrupt, syscall.SIGTERM) + + ctx, cancel := context.WithCancel(context.Background()) + var wg sync.WaitGroup + // Channel to signal polling to reset round event counter + resetEventCounter := make(chan struct{}) + defer close(resetEventCounter) + lastValue := 0 newRoundTrigger := time.NewTimer(0) // Want to trigger a new round ASAP defer newRoundTrigger.Stop() o.setFilterQuery() - err := o.observeOCREvents() - require.NoError(o.t, err, "Error subscribing to OCR events") + wg.Add(1) + go o.pollingOCREvents(ctx, &wg, resetEventCounter) n := o.Config.GetNetworkConfig() @@ -709,6 +716,8 @@ func (o *OCRSoakTest) testLoop(testDuration time.Duration, newValue int) { o.deleteChaosSimulations() os.Exit(interruptedExitCode) // Exit with interrupted code to indicate test was interrupted, not just a normal failure case <-endTest: + cancel() + wg.Wait() // Wait for polling to complete return case <-newRoundTrigger.C: err := o.triggerNewRound(newValue) @@ -719,6 +728,8 @@ func (o *OCRSoakTest) testLoop(testDuration time.Duration, newValue int) { Str("Waiting", timerReset.String()). Msg("Error triggering new round, waiting and trying again. Possible connection issues with mockserver") } + // Signal polling to reset event counter + resetEventCounter <- struct{}{} newRoundTrigger.Reset(timerReset) // Change value for the next round @@ -824,75 +835,171 @@ func (o *OCRSoakTest) setFilterQuery() { Msg("Filter Query Set") } -// observeOCREvents subscribes to OCR events and logs them to the test logger -// WARNING: Should only be used for observation and logging. This is not a reliable way to collect events. -func (o *OCRSoakTest) observeOCREvents() error { - eventLogs := make(chan types.Log) - ctx, cancel := context.WithTimeout(testcontext.Get(o.t), 5*time.Second) - eventSub, err := o.seth.Client.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) - cancel() - if err != nil { - return err - } +// pollingOCREvents Polls the blocks for OCR events and logs them to the test logger +func (o *OCRSoakTest) pollingOCREvents(ctx context.Context, wg *sync.WaitGroup, resetEventCounter <-chan struct{}) { + defer wg.Done() + // Keep track of the last processed block number + processedBlockNum := o.startingBlockNum - 1 + // TODO: Make this configurable + pollInterval := time.Second * 30 + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + + // Retrieve expected number of events per round from configuration + expectedEventsPerRound := *o.Config.GetActiveOCRConfig().Common.NumberOfContracts + eventCounter := 0 + roundTimeout := o.Config.GetActiveOCRConfig().Soak.TimeBetweenRounds.Duration + timeoutTimer := time.NewTimer(roundTimeout) + round := 0 + defer timeoutTimer.Stop() + + o.log.Info().Msg("Start Polling for Answer Updated Events") - go func() { - for { - select { - case event := <-eventLogs: - if o.OCRVersion == "1" { - answerUpdated, err := o.ocrV1Instances[0].ParseEventAnswerUpdated(event) - if err != nil { - o.log.Warn(). - Err(err). - Str("Address", event.Address.Hex()). - Uint64("Block Number", event.BlockNumber). - Msg("Error parsing event as AnswerUpdated") - continue - } - o.log.Info(). - Str("Address", event.Address.Hex()). - Uint64("Block Number", event.BlockNumber). - Uint64("Round ID", answerUpdated.RoundId.Uint64()). - Int64("Answer", answerUpdated.Current.Int64()). - Msg("Answer Updated Event") - } else if o.OCRVersion == "2" { - answerUpdated, err := o.ocrV2Instances[0].ParseEventAnswerUpdated(event) - if err != nil { - o.log.Warn(). - Err(err). - Str("Address", event.Address.Hex()). - Uint64("Block Number", event.BlockNumber). - Msg("Error parsing event as AnswerUpdated") - continue - } + for { + select { + case <-resetEventCounter: + if round != 0 { + if eventCounter == expectedEventsPerRound { o.log.Info(). - Str("Address", event.Address.Hex()). - Uint64("Block Number", event.BlockNumber). - Uint64("Round ID", answerUpdated.RoundId.Uint64()). - Int64("Answer", answerUpdated.Current.Int64()). - Msg("Answer Updated Event") + Int("Events found", eventCounter). + Int("Events Expected", expectedEventsPerRound). + Msg("All expected events found") + } else if eventCounter < expectedEventsPerRound { + o.log.Warn(). + Int("Events found", eventCounter). + Int("Events Expected", expectedEventsPerRound). + Msg("Expected to find more events") } - case err = <-eventSub.Err(): - backoff := time.Second - for err != nil { - o.log.Info(). - Err(err). - Str("Backoff", backoff.String()). - Interface("Query", o.filterQuery). - Msg("Error while subscribed to OCR Logs. Resubscribing") - ctx, cancel = context.WithTimeout(testcontext.Get(o.t), backoff) - eventSub, err = o.seth.Client.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) - cancel() - if err != nil { - time.Sleep(backoff) - backoff = time.Duration(math.Min(float64(backoff)*2, float64(30*time.Second))) - } + } + // Reset event counter and timer for new round + eventCounter = 0 + // Safely stop and drain the timer if a value is present + if !timeoutTimer.Stop() { + <-timeoutTimer.C + } + timeoutTimer.Reset(roundTimeout) + o.log.Info().Msg("Polling for new round, event counter reset") + round++ + case <-ctx.Done(): + o.log.Info().Msg("Test duration ended, finalizing event polling") + timeoutTimer.Reset(roundTimeout) + // Wait until expected events are fetched or until timeout + for eventCounter < expectedEventsPerRound { + select { + case <-timeoutTimer.C: + o.log.Warn().Msg("Timeout reached while waiting for final events") + return + case <-ticker.C: + o.fetchAndProcessEvents(&eventCounter, expectedEventsPerRound, &processedBlockNum) } } + o.log.Info(). + Int("Events found", eventCounter). + Int("Events Expected", expectedEventsPerRound). + Msg("Stop polling.") + return + case <-ticker.C: + o.fetchAndProcessEvents(&eventCounter, expectedEventsPerRound, &processedBlockNum) } - }() + } +} - return nil +// Helper function to poll events and update eventCounter +func (o *OCRSoakTest) fetchAndProcessEvents(eventCounter *int, expectedEvents int, processedBlockNum *uint64) { + latestBlock, err := o.seth.Client.BlockNumber(context.Background()) + if err != nil { + o.log.Error().Err(err).Msg("Error getting latest block number") + return + } + + if *processedBlockNum == latestBlock { + o.log.Debug(). + Uint64("Latest Block", latestBlock). + Uint64("Last Processed Block Number", *processedBlockNum). + Msg("No new blocks since last poll") + return + } + + // Check if the latest block is behind processedBlockNum due to possible reorgs + if *processedBlockNum > latestBlock { + o.log.Error(). + Uint64("From Block", *processedBlockNum). + Uint64("To Block", latestBlock). + Msg("The latest block is behind the processed block. This could happen due to RPC issues or possibly a reorg") + *processedBlockNum = latestBlock + return + } + + fromBlock := *processedBlockNum + 1 + o.filterQuery.FromBlock = big.NewInt(0).SetUint64(fromBlock) + o.filterQuery.ToBlock = big.NewInt(0).SetUint64(latestBlock) + + o.log.Debug(). + Uint64("From Block", fromBlock). + Uint64("To Block", latestBlock). + Msg("Fetching logs for the specified range") + + logs, err := o.seth.Client.FilterLogs(context.Background(), o.filterQuery) + if err != nil { + o.log.Error().Err(err).Msg("Error fetching logs") + return + } + + for _, event := range logs { + *eventCounter++ + if o.OCRVersion == "1" { + answerUpdated, err := o.ocrV1Instances[0].ParseEventAnswerUpdated(event) + if err != nil { + o.log.Warn(). + Err(err). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Msg("Error parsing event as AnswerUpdated") + continue + } + if *eventCounter <= expectedEvents { + o.log.Info(). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Uint64("Round ID", answerUpdated.RoundId.Uint64()). + Int64("Answer", answerUpdated.Current.Int64()). + Msg("Answer Updated Event") + } else { + o.log.Error(). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Uint64("Round ID", answerUpdated.RoundId.Uint64()). + Int64("Answer", answerUpdated.Current.Int64()). + Msg("Excess event detected, beyond expected count") + } + } else if o.OCRVersion == "2" { + answerUpdated, err := o.ocrV2Instances[0].ParseEventAnswerUpdated(event) + if err != nil { + o.log.Warn(). + Err(err). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Msg("Error parsing event as AnswerUpdated") + continue + } + if *eventCounter <= expectedEvents { + o.log.Info(). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Uint64("Round ID", answerUpdated.RoundId.Uint64()). + Int64("Answer", answerUpdated.Current.Int64()). + Msg("Answer Updated Event") + } else { + o.log.Error(). + Str("Address", event.Address.Hex()). + Uint64("Block Number", event.BlockNumber). + Uint64("Round ID", answerUpdated.RoundId.Uint64()). + Int64("Answer", answerUpdated.Current.Int64()). + Msg("Excess event detected, beyond expected count") + } + } + } + *processedBlockNum = latestBlock } // triggers a new OCR round by setting a new mock adapter value @@ -941,6 +1048,9 @@ func (o *OCRSoakTest) collectEvents() error { o.ocrRoundStates[len(o.ocrRoundStates)-1].EndTime = start // Set end time for last expected event o.log.Info().Msg("Collecting on-chain events") + // Set from block to be starting block before filtering + o.filterQuery.FromBlock = big.NewInt(0).SetUint64(o.startingBlockNum) + // We must retrieve the events, use exponential backoff for timeout to retry timeout := time.Second * 15 o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") diff --git a/tools/bin/goreleaser_utils b/tools/bin/goreleaser_utils index b4b7f124ba7..52e37cefd51 100755 --- a/tools/bin/goreleaser_utils +++ b/tools/bin/goreleaser_utils @@ -27,7 +27,7 @@ before_hook() { # linux_arm64, rather than being suffixless on native platforms if [ "$GOARCH" = "arm64" ]; then if [ -d "$BIN_DIR/linux_arm64" ]; then - cp "$BIN_DIR/linux_arm64"/chainlink* "$PLUGIN_DIR" + cp "$BIN_DIR/linux_arm64_v8.0"/chainlink* "$PLUGIN_DIR" else cp "$BIN_DIR"/chainlink* "$PLUGIN_DIR" fi