Skip to content

review(ens): pre-draft calldata verification for kpk Endowment permissions Update #10 - #102

Open
alextnetto wants to merge 2 commits into
mainfrom
ens/kpk-endowment-permissions-update-10
Open

review(ens): pre-draft calldata verification for kpk Endowment permissions Update #10#102
alextnetto wants to merge 2 commits into
mainfrom
ens/kpk-endowment-permissions-update-10

Conversation

@alextnetto

Copy link
Copy Markdown
Member

Summary

Pre-draft calldata security review of Endowment permissions to KPK Update #10.

There is no Tally draft yet. The reviewed artifact is the Safe Transaction Builder payload published with the forum post: karpatkey/client-configs @ ens-dao-manager-harvest-rwa-yield, commit 3e447f8d21 (clients/ens-dao/mainnet/payloads/ensPermissionsUpdate10.json, 54 transactions).

What the test does

  • _generateCallData() manually derives the full 54-transaction MultiSend body from the forum specification and Solidity interfaces (no hex blobs, every selector from an interface, every address a named constant)
  • _assertDerivedPayloadMatches() diffs the derivation byte for byte against expectedMultiSend.txt, the published payload re-encoded verbatim (verified with a negative control: a one-byte change fails the test)
  • _beforeProposal() proves the new venues are unreachable today and that the three reward distributors are already claimable with payouts pinned to the Safe
  • _afterExecution() asserts every new permission positively and negatively: vault deposit/withdraw/redeem pinned to the Safe, Aave Horizon pinned to RLUSD, Pendle pinned to the PT-sUSDS-26NOV2026 market with the external-router escape hatch shut, approve() spenders pinned, and 19 pre-existing approvals preserved

Verification result

Derived calldata matches the published payload exactly. Permission shapes are tight. No permissions removed (confirmed by simulation and by the additions-only diff of roles/MANAGER in karpatkey's config repo).

Findings (see forum-post.md)

  1. The payload deploys a karpatkey-owned sub-Roles Modifier chained under MANAGER, undisclosed in the forum post
  2. Spec items 5 (syrup tokens on CoW lists) and 6 (Harvest role) are absent from the payload
  3. The item 6 distributor claims already exist on MANAGER today, recipient-pinned
  4. The forum table lists a Steakhouse address with no code on mainnet; the payload uses the correct vault

Recommendation: NEEDS_REVIEW. Questions for kpk are drafted in forum-post.md.

Test

forge test --match-path "src/ens/proposals/ep-kpk-update-10/*" -vv

1 passed. Will be re-run against the final payload when the Tally draft is published.

@alextnetto
alextnetto force-pushed the ens/kpk-endowment-permissions-update-10 branch from 3df746f to 74faa82 Compare August 1, 2026 16:46

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3df746fa89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +615 to +616
_assertTargetNotAllowed(SYRUP_USDC, _approveCall(GPV2_VAULT_RELAYER));
_assertTargetNotAllowed(SYRUP_USDT, _approveCall(GPV2_VAULT_RELAYER));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Test the CoW order token lists directly

If the finalized payload implements item 5 by adding syrupUSDC and syrupUSDT to the existing ICowSwapOrderSigner.signOrder sell/buy-token conditions, these calls will still target the syrup token contracts themselves and remain TargetAddressNotAllowed. Consequently, the assertions will keep passing even though the documented gap has been fixed, contrary to the stated expectation that the test should flag the update. Exercise signOrder with each syrup token as the sell and buy token instead; token approval permissions are independent of the CoW token lists.

Useful? React with 👍 / 👎.

Comment on lines +149 to +150
Unlike #14, **#15 is still Queued** (`state(15) = Queued`); the node shows its pools still point at their old
controllers, so this batch has **not** taken effect yet — there is still a window to revoke before it can execute. ✅

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Refresh the expired proposal-state snapshot

For anyone relying on this report after the reviewed commit date, this promise of a remaining pre-execution window is already inconsistent with the report's own timeline: proposal #15 was created on July 9, and §5 says its createTime → eta interval is exactly seven days, while this commit is dated August 1. The block-25,535,332 snapshot can therefore no longer establish that the proposal is still pre-executable; by publication it must have advanced to Grace, Executed, or Expired. Refresh the state at publication time and timestamp the result instead of telling exposed users that they still have time before execution.

Useful? React with 👍 / 👎.

Comment on lines +8 to +9
We reconstructed all 54 transactions of the payload from the specification and from the public contract interfaces, and
compared our reconstruction against the published payload byte for byte. The two are identical. The permissions granted

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Disclose the payload-derived reconstruction inputs

The claim that all 54 transactions were reconstructed from the specification and public interfaces cannot hold for this payload: the same post says transactions 0–7 deploy and configure a sub-Roles module that the specification does not mention, and the README says transaction 53's annotation content was extracted from the payload; the unmentioned approval-list extensions likewise are not derivable from the specification. The byte comparison still validates the hand encoding, but presenting it as a wholly specification-derived reconstruction overstates its independence. Identify which fields came from the executable payload or configuration repository and reserve the specification-derived claim for the transactions it actually describes.

Useful? React with 👍 / 👎.

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.

1 participant