Skip to content

refactor: introduce shared action layer (CctActions + EoaExecutor) under the setup scripts, with parity tests#4

Merged
SyedAsadKazmi merged 1 commit into
mainfrom
refactor/cct-actions-eoa-executor-layer
Jul 8, 2026
Merged

refactor: introduce shared action layer (CctActions + EoaExecutor) under the setup scripts, with parity tests#4
SyedAsadKazmi merged 1 commit into
mainfrom
refactor/cct-actions-eoa-executor-layer

Conversation

@aelmanaa

@aelmanaa aelmanaa commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What improves for the user

Before: every script builds calldata inline, so the same encoding logic is duplicated across scripts and there is no way to reuse an operation outside its one script.

After: every README command works identically (parity-tested, byte for byte), and each operation's calldata is built in exactly one audited place — which is what makes future governance execution modes (multisig batch, timelock) and lifecycle flows possible without re-implementing any operation. The visible change for a user is zero; the change for a reviewer is that one file now answers "what does this operation send on-chain."

What this PR does

This PR sets the pattern a follow-up rollout PR replicates across the remaining script groups (configure, hooks/allowlist, operations) — please review it line by line; design feedback lands here, before the pattern is stamped across the repo.

  1. src/actions/CctActions.sol — the shared action layer. A Call {target, value, data} struct plus one pure builder per setup write operation, each encoded with abi.encodeCall on the real contract interfaces (never a hand-written selector): registerAdminViaOwner and registerAdminViaGetCCIPAdmin (two builders for the two RegistryModuleOwnerCustom claim paths — the owner-vs-getCCIPAdmin probe stays script-side in ClaimAdmin), acceptAdminRole, applyChainUpdates (takes the already-encoded remote pool/token bytes, so chain-family encoding stays in ChainHandlers), setPool, transferAdminRole, transferOwnership, acceptOwnership, plus the token-admin handoff builders the transfer-ownership scripts need (beginDefaultAdminTransfer, acceptDefaultAdminTransfer, grantRole/revokeRole/handOffRole).
    Includes registerAndAcceptAdmin* one-batch conveniences: the claim sets the registry's pending administrator to the calling account, so claim + accept execute correctly as ONE atomic batch (proven by test_RegistrationPair_ExecutesAsOneBatch).
  2. src/base/EoaExecutor.s.sol — the EOA execution mode: takes a Call[], vm.startBroadcast(), executes each call in order (bubbling the underlying revert reason unchanged), and logs each target + selector.
  3. Setup write scripts refactored onto the buildersClaimAdmin, AcceptAdminRole, ApplyChainUpdates (both JSON and env modes), SetPool, TransferTokenAdminRole, transfer-ownership/TransferOwnership, transfer-ownership/AcceptOwnership. Each parses inputs exactly as today, calls the matching builder, and hands the result to the executor. External behavior, env vars, and console output are unchanged (the only console addition is the executor's target+selector line). The read-only setup scripts (GetSupportedChains, GetTokenConfig, GetTypeAndVersion) are untouched.
  4. Committed script/input/apply-chain-updates.json example fixed (closes the known issue from the CI PR): the ARBITRUM_SEPOLIA entry had no destChainSelector and HelperConfig does not know that chain, so JSON mode with the committed example reverted (Chain selector is 0 for remoteChains[1]). The entry now uses PLUME_TESTNET (a chain HelperConfig serves), and a test runs JSON mode with the committed example itself, not only a test fixture.

Parity-test methodology (how "the refactor changed nothing" is proven)

The OLD (pre-refactor) behavior was captured first: each parity test replays the exact calls the pre-refactor script made (direct typed calls from the same signer), snapshots the resulting fork state, reverts, then runs the REFACTORED script and asserts the identical end state. Where encoding matters, the builder output is additionally pinned byte-for-byte against a hand-written abi.encodeCall expectation.

New suite test/actions/SetupActions.t.sol (8 tests), extending BaseForkTest (public-RPC default, zero config):

  • applyChainUpdates calldata parity for an EVM remote (abi.encode(address)) AND an SVM remote (raw 32 bytes from an independent base58 decode) vs hand-encoded abi.encodeCall(TokenPool.applyChainUpdates, (...)).
  • setPool calldata parity + fork registry-state parity (old inline registry.setPool vs the refactored script).
  • ClaimAdmin + AcceptAdminRole fork-state parity (TokenConfig administrator end state), plus the registration pair as ONE batch.
  • TransferOwnership/AcceptOwnership two-step on a fork: pool Ownable2Step (both scripts exercised), and the token default-admin two-step (beginDefaultAdminTransfer → warp past the transfer schedule → accept). Note: the ownership scenarios run inside one test function on purpose — forge runs tests in parallel and vm.setEnv is process-wide, so tests setting different ENTITY_TYPE/ADDRESS/NEW_OWNER values would race (same lesson as the CI PR's parallel-suite note; the same fix serializes the two JSON-input-file phases in ApplyChainUpdates.t.sol).
  • test/setup/ApplyChainUpdates.t.sol keeps the pre-existing fixture-JSON state assertions passing against the refactored script, and gains the committed-example phase (asserts MANTLE_SEPOLIA, PLUME_TESTNET, SOLANA_DEVNET all configured, incl. the two-remote-pool entry).

Acceptance checklist

  • Every script/setup/ README command runs unchanged against a fork — each refactored script was dry-run (no --broadcast) on a local Sepolia fork with the README's exact invocation, before and after the refactor; the console output diff is empty except the executor's added Executing call i/n: target … selector … line, and the end state (registry pool, TAR administrator, pool owner, token default admin) is identical. Sample dry-run log delta below.
  • Calldata parity test green: builder output equals hand-encoded expectation for both an EVM remote and an SVM remote input.
  • Fork state parity green: same end state as pre-refactor for ApplyChainUpdates and SetPool (and the claim/accept + ownership flows).
  • The committed apply-chain-updates.json example runs clean in JSON mode (known issue closed; pre-fix it exited 1 with Chain selector is 0 for remoteChains[1], post-fix the same invocation completes simulation, exit 0).
  • No README command syntax changed (no README edits needed — commands, env vars, and modes are identical).
  • forge build, forge fmt --check, forge lint, forge test all green locally: 22 tests (14 existing kept passing + 8 new tests in SetupActions.t.sol; the pre-existing JSON-mode test additionally gained the committed-example phase).
Sample dry-run log delta (ClaimAdmin, old vs refactored — README command, no --broadcast)
14a15
>     Executing call 1/1: target 0xa3c796d480638d7476792230da1E2ADa86e031b0 selector 0xff12c354

Same shape for every other setup script: AcceptAdminRole (0x156194da), ApplyChainUpdates (0xe8a1da17), SetPool (0x4e847fc7), TransferTokenAdminRole (0xddadfa8e), TransferOwnership pool (0xf2fde38b) / token (0x634e93da), AcceptOwnership pool (0x79ba5097) / token (0xcefc1429).

Test plan

  • forge build
  • forge fmt --check
  • forge lint
  • forge test

Out of scope: deploy scripts (they create contracts and stay broadcast scripts by nature — not Call[] material); all configure/hooks/operations scripts (the follow-up rollout PR); anything under .github/.

@aelmanaa aelmanaa requested review from a team as code owners July 7, 2026 18:15
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 aelmanaa, thanks for creating this pull request!

To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team.

Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks!

@SyedAsadKazmi SyedAsadKazmi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tested the existing functionalities of the CCT flow E2E.

LGTM. Thanks!

@SyedAsadKazmi SyedAsadKazmi merged commit ac9dd00 into main Jul 8, 2026
9 checks passed
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.

2 participants