Payout Router + CreatorCoin Control Hardening (March 2026)
Scope
This change set hardens deployment-time and runtime control for payout routing, CreatorCoin admin ownership, and keeper automation when vaults switch payout flow from creator wallet lanes to protocol-governed lanes.
Primary goals:
- Ensure routed assets are actually processable by the payout path (
creatorCoin,ZORA, and claimed protocol rewards). - Remove creator-default admin control where protocol governance should own critical knobs.
- Eliminate silent skips for critical payout recipient and ownership handoffs.
What Was Implemented
1) New CreatorCoin policy controller (protocol-owned)
Added:
contracts/utilities/routers/CreatorCoinPolicyController.sol
Behavior:
- Holds CreatorCoin ownership after deployment.
- Allows protocol owner to:
- enforce
setPayoutRecipient(payoutRouter)viaenforcePayoutRouter() - migrate CreatorCoin ownership via
transferCreatorCoinOwnership(newOwner)
- enforce
This creates a dedicated, explicit admin surface for CreatorCoin policy instead of leaving CreatorCoin owner as an EOA creator lane.
2) Deployment ownership hardening
Updated deployment flow in:
frontend/src/pages/deploy/DeployVault.tsxfrontend/api/_handlers/_paymaster.tscontracts/helpers/batchers/DeploymentBatcher.sol
Key outcomes:
PayoutRouterowner is expected/validated as protocol treasury (not creator).setPayoutRecipient(expectedPayoutRouter)is no longer a silent best-effort path; mismatch/unauthorized execution is treated as a hard policy failure in deploy flow/paymaster checks.- CreatorCoin ownership transfer target is enforced to the expected
CreatorCoinPolicyController. - Paymaster now enforces phase tuple policy fields and codeIds (not only selector allowlists), including:
- phase-2 deploy lanes (
creatorTreasury,payoutRecipient) - phase-3 keeper lanes (
ajnaKeeper,solanaKeeper) - canonical phase-2 / phase-3 codeId sets
- phase-2 deploy lanes (
- Phase-3 strategy deploy now binds auth to actual onchain vault owner (
vault.owner() == params.owner). - Ajna auth admin handoff is protocol treasury.
- CCA migration config and treasury lanes in phase-2/phase-3 paths are protocol-owned by default.
Onchain policy tightening in DeploymentBatcher:
- phase-2 rejects non-protocol
creatorTreasuryoverrides - phase-2 rejects non-zero
payoutRecipient(recipient wiring is now explicit via router/policy-controller path)
3) Router execution path support for protocol rewards
Updated:
contracts/utilities/routers/PayoutRouter.sol
Added support:
protocolRewardsClaimable()claimProtocolRewards(amount)claimAllProtocolRewards()- Compatibility handling for multiple protocol-rewards withdraw ABIs.
Net effect: protocol rewards can be claimed into router flow, then converted/queued through the existing convertAndQueue path.
4) Keeper auto-config + runtime config exposure
Updated:
frontend/api/_handlers/deploy/_config.tsfrontend/server/_lib/payoutRouterRuntime.ts.env.examplefrontend/.env.examplecre/secrets.example.env
What this provides:
- Canonical server-side keeper resolution for payout-router admin/config:
PAYOUT_ROUTER_KEEPER- fallback
CRE_KEEPER_ADDRESS - fallback
KEEPR_ADDRESS - final fallback from
KEEPR_PRIVATE_KEY
- Deploy config API now returns payout-router keeper + fee config used by auto-setup/validation.
5) CRE automation job for convert/queue processing
Added:
cre/actions/payout-router-harvest.action.tscre/workflows/payout-router-harvest.workflow.ts
Integrated into unified workflow:
cre/workflows/4626.workflow.tscre/runner.tscre/package.jsoncre/utils/registry.ts
Job behavior (per vault with router configured):
- Optional claim of protocol rewards.
convertAndQueueforcreatorCoin.convertAndQueueforZORA.- Optional
convertAndQueueforWETH(for claimed rewards path).
6) Legacy strategy-batcher lane protocolized
Updated:
contracts/helpers/batchers/StrategyDeploymentBatcher.sol
Hardening:
batchDeployStrategiesis now protocol-owner-gated (onlyProtocolOwner).- This prevents arbitrary caller-controlled strategy ownership/admin setup through that legacy surface.
Role / Ownership Posture After This Change
Protocol-controlled by default:
PayoutRouter.owner(critical router admin surface).CreatorCoinPolicyController.owner(CreatorCoin policy admin).- Ajna auth admin handoff target.
- CCA migration config treasury lanes.
StrategyDeploymentBatcher.batchDeployStrategiescaller lane.
Creator-controlled where still intended:
- Vault final ownership remains creator lane after phase-2 finalize.
Tests and Validation
Solidity tests
Executed and passing:
forge test --match-contract "DeploymentBatcher.*|CompileTestBatcher"- Result:
22 passed, 0 failed.
Notes:
- Phase-3 tests were updated to reflect new invariants:
- mocked vaults now expose
owner() - Ajna auth admin expected as protocol treasury
- mocked vaults now expose
Updated test files:
test/DeploymentBatcher.Phase3Ownership.t.soltest/DeploymentBatcher.SolanaStrategyPhase3.t.soltest/DeploymentBatcher.ThreeWaySplit.t.sol
Frontend API tests
Executed and passing:
pnpm -C frontend exec vitest run api/__tests__/paymasterDeploySessionSetup.test.ts api/__tests__/paymasterLegacyWithdraw.test.ts api/__tests__/paymasterPhase2Finalize.test.ts- Result:
3 files passed, 21 tests passed.
Follow-up Items
- Run full
forge testin a non-interrupted session for complete suite confirmation. - Add dedicated unit tests for new
PayoutRouterprotocol-rewards claim methods. - Add explicit regression tests for
CreatorCoinPolicyControllerdeployment + transfer path.