Skip to content

script: Add missing script for transferring ownership of CCIP token pool - #277

Merged
0xJem merged 1 commit into
developfrom
ccip-bridge-script-fix
May 5, 2026
Merged

0xJem merged 1 commit into
developfrom
ccip-bridge-script-fix

Conversation

@0xJem

@0xJem 0xJem commented May 5, 2026 •

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features
    • Added batch operation to transfer token pool ownership to the DAO multisig with validation logic to prevent redundant transfers
    • Added batch operation to complete token pool ownership acceptance by the DAO multisig with verification safeguards

@0xJem 0xJem self-assigned this May 5, 2026
@coderabbitai

coderabbitai Bot commented May 5, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR adds two batch action functions to CCIPTokenPoolBatch for managing LockReleaseTokenPool ownership transfer through a DAO multisig: transferTokenPoolOwnershipToDaoMS() initiates the transfer, and acceptTokenPoolOwnership() completes it after verifying current ownership state.

Changes

Ownership Transfer Batch Actions

Layer / File(s) Summary
Import & Interface
src/scripts/ops/batches/CCIPTokenPool.sol
Adds Ownable2Step import from Chainlink shared access module for two-step ownership pattern.
Transfer Initiation
src/scripts/ops/batches/CCIPTokenPool.sol
transferTokenPoolOwnershipToDaoMS() retrieves token pool and DAO multisig address, verifies current owner differs from DAO, enqueues transferOwnership() call, and executes batch with completion logging.
Transfer Acceptance
src/scripts/ops/batches/CCIPTokenPool.sol
acceptTokenPoolOwnership() retrieves token pool and DAO multisig address, verifies DAO is pending owner, enqueues acceptOwnership() call, and executes batch with completion logging.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Two functions hopping in with grace,
Transfer ownership from place to place!
A check, a batch, a proposal spun,
The DAO's chain of trust is done. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'script: Add missing script for transferring ownership of CCIP token pool' accurately describes the main change—adding new script functions for transferring and accepting CCIP token pool ownership.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ccip-bridge-script-fix

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/scripts/ops/batches/CCIPTokenPool.sol (2)

224-232: 💤 Low value

Consider casting to Ownable2Step for clarity.

The code only uses owner() and transferOwnership() from the Ownable2Step interface. Casting to Ownable2Step directly would be more accurate and would work correctly for both LockReleaseTokenPool (canonical) and BurnMintTokenPool (non-canonical) since both inherit Ownable2Step.

♻️ Suggested refactor
-        if (LockReleaseTokenPool(tokenPool).owner() == daoMS) {
+        if (Ownable2Step(tokenPool).owner() == daoMS) {
             console2.log("Owner already transferred to", daoMS, ". Skipping.");
             return;
         }

         console2.log("Transferring ownership of", tokenPool, "to", daoMS);
         addToBatch(
             tokenPool,
             abi.encodeWithSelector(Ownable2Step.transferOwnership.selector, daoMS)
         );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scripts/ops/batches/CCIPTokenPool.sol` around lines 224 - 232, Replace
the raw LockReleaseTokenPool calls with an explicit Ownable2Step cast: use
Ownable2Step(tokenPool).owner() for the owner check and encode the call with
abi.encodeWithSelector(Ownable2Step.transferOwnership.selector, daoMS) before
calling addToBatch; update the if-check and the addToBatch invocation that
reference tokenPool and LockReleaseTokenPool to use Ownable2Step(tokenPool) so
both LockReleaseTokenPool and BurnMintTokenPool are handled clearly.

244-262: ⚡ Quick win

Consider verifying pendingOwner() before attempting to accept ownership.

The function checks if owner() == daoMS for early exit but doesn't verify that pendingOwner() == daoMS before adding the batch action. If ownership was not transferred to daoMS (or was transferred to someone else), the transaction will revert on-chain with a less informative error.

Adding a pendingOwner() check would provide clearer feedback when the script is run in an incorrect state.

Also, line 255 log message "Accepting ownership of X to Y" is grammatically awkward—consider "for" or restructure.

♻️ Suggested improvement
     function acceptTokenPoolOwnership() external setUpWithChainId(false) {
         address tokenPool = _getTokenPoolAddressNotZero(chain);
         address daoMS = _envAddressNotZero("olympus.multisig.dao");

         // Check if the owner is already the DAO MS
-        if (LockReleaseTokenPool(tokenPool).owner() == daoMS) {
+        if (Ownable2Step(tokenPool).owner() == daoMS) {
             console2.log("Owner already transferred to", daoMS, ". Skipping.");
             return;
         }

-        console2.log("Accepting ownership of", tokenPool, "to", daoMS);
+        // Check if the pending owner is the DAO MS
+        if (Ownable2Step(tokenPool).pendingOwner() != daoMS) {
+            console2.log("Pending owner is not", daoMS, ". Skipping.");
+            return;
+        }
+
+        console2.log("Accepting ownership of", tokenPool, "for", daoMS);
         addToBatch(tokenPool, abi.encodeWithSelector(Ownable2Step.acceptOwnership.selector));
Does Chainlink CCIP Ownable2Step have a pendingOwner() public getter function?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scripts/ops/batches/CCIPTokenPool.sol` around lines 244 - 262, In
acceptTokenPoolOwnership(), check that the pending owner is daoMS before queuing
the acceptOwnership call: retrieve pendingOwner() from
LockReleaseTokenPool(tokenPool) and if it is not daoMS log a clear message and
return instead of adding the batch; keep the existing owner() equality check
first. Also adjust the console2.log message from "Accepting ownership of X to Y"
to use "for" or rephrase to "Accepting ownership of <tokenPool> for <daoMS>" to
be grammatically correct; ensure you still call addToBatch(tokenPool,
abi.encodeWithSelector(Ownable2Step.acceptOwnership.selector)) and
proposeBatch() only when pendingOwner() == daoMS.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/scripts/ops/batches/CCIPTokenPool.sol`:
- Around line 224-232: Replace the raw LockReleaseTokenPool calls with an
explicit Ownable2Step cast: use Ownable2Step(tokenPool).owner() for the owner
check and encode the call with
abi.encodeWithSelector(Ownable2Step.transferOwnership.selector, daoMS) before
calling addToBatch; update the if-check and the addToBatch invocation that
reference tokenPool and LockReleaseTokenPool to use Ownable2Step(tokenPool) so
both LockReleaseTokenPool and BurnMintTokenPool are handled clearly.
- Around line 244-262: In acceptTokenPoolOwnership(), check that the pending
owner is daoMS before queuing the acceptOwnership call: retrieve pendingOwner()
from LockReleaseTokenPool(tokenPool) and if it is not daoMS log a clear message
and return instead of adding the batch; keep the existing owner() equality check
first. Also adjust the console2.log message from "Accepting ownership of X to Y"
to use "for" or rephrase to "Accepting ownership of <tokenPool> for <daoMS>" to
be grammatically correct; ensure you still call addToBatch(tokenPool,
abi.encodeWithSelector(Ownable2Step.acceptOwnership.selector)) and
proposeBatch() only when pendingOwner() == daoMS.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e8e20d99-e453-415a-8f0c-97c3f721ae87

📥 Commits

Reviewing files that changed from the base of the PR and between a385067 and af9bc7b.

📒 Files selected for processing (1)
  • src/scripts/ops/batches/CCIPTokenPool.sol

@0xJem
0xJem requested a review from zeroxnoodle May 5, 2026 12:54
@0xJem
0xJem merged commit 8e84c20 into develop May 5, 2026
13 of 15 checks passed
@0xJem
0xJem deleted the ccip-bridge-script-fix branch May 5, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants