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