[Executable] Treasury Flow Automation

Abstract

ENS protocol revenue currently requires three separate steps to move from registrar controllers to productive use in the endowment and fund operations. This proposal introduces a Registrar Manager contract that takes ownership of all registrar controllers and enables permissionless withdrawals directly to the endowment. It also configures a Zodiac module permission on the endowment to allow the treasury manager to send ETH and USDC to the timelock without a proposal, following a two-year stablecoin runway policy consistent with the current Investment Policy Statement.

The result is zero proposals for routine treasury operations, faster yield on collected revenue, permissionless withdrawals, and increased income for the DAO. A conservative estimate suggests that since January 2024, approximately $1 million in yield was missed due to idle capital in registrar controllers and the timelock.

Specification

Problem

ENS protocol revenue requires three steps to move from collection to productive use.
Registrar controllers collect ETH from .eth registrations and renewals, and the current controller (controller.ens.eth) already sends withdrawn ETH to the timelock. However, the old registrar controller at 0x283Af0B28c62C092C9727F1Ee09c02CA627eb7F5 holds roughly 772 ETH that requires a dedicated proposal just to withdraw.

Once ETH reaches the timelock, sending it to the endowment (endowment.ensdao.eth) for investment requires another proposal. Capital can sit idle in the timelock for several months before it starts earning yield.

When the DAO needs to fund operational expenses like ENS Labs, the Service Provider Program, or Working Groups, yet another proposal is needed to transfer from the endowment back to the timelock or to execute operations with twap.ensdao.eth. For context, EP 6.32 proposed a $2.5M USDC transfer from the endowment to the timelock just to cover Working Group budgets that had already been approved.

On top of that, there is currently roughly 4,148 ETH sitting in the timelock that could be earning yield in the endowment. The operational funding process today is reactive and not smooth. Each transfer requires a full governance cycle, and capital that could be generating yield sits idle throughout.

Key Addresses

• Timelock: 0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7 (wallet.ensdao.eth)
• Current Registrar Controller: 0x253553366Da8546fC250F225fE3d25d0C782303b (controller.ens.eth)
• Old Registrar Controller (~772 ETH): 0x283Af0B28c62C092C9727F1Ee09c02CA627eb7F5
• Endowment: 0x4F2083f5fBede34C2714aFfb3105539775f7FE64 (endowment.ensdao.eth)
• Governor: 0x323A76393544d5ecca80cd6ef2A560C6a395b7E3 (governor.ensdao.eth)
• TWAP safe: 0x02D61347e5c6EA5604f3f814C5b5498421cEBdEB (twap.ensdao.eth)

Solution

This proposal introduces two components that eliminate all three bottlenecks: a Registrar Manager and an Endowment Zodiac Module permission.

Registrar Manager

The Registrar Manager is a contract that becomes the owner of all registrar controllers. Its key properties:

a) Exposes a permissionless withdraw() function that anyone can call. When called, it pulls ETH from each controller and routes it directly to the endowment.

b) The destination address is configurable by the DAO through the timelock, so governance can redirect the flow at any time.

c) Acts as a pass-through for governance calls, meaning the DAO retains full control over controller parameters.

d) New controllers can be added over time.

e) Never holds funds.

Source code of Registrar Manager contract.

Endowment Zodiac Module

The Zodiac module is already part of how the endowment is managed. This proposal adds a scoped permission that allows the treasury manager (Karpatkey) to send ETH and USDC to wallet.ensdao.eth without a proposal. It cannot send to any other address or use any other token. All other endowment operations remain unchanged.

Funding Policy

This is the current suggestion for the funding policy.

The timelock maintains a 6 months runway in USDC (~$8M at current spending). Each quarter, Karpatkey calculates the current runway on the timelock. If it falls below 6 months, the endowment sends the shortfall. If it exceeds 6 months, no transfer is needed and the excess stays invested in the endowment. If an additional governance proposal requiring capital is approved, this may trigger an earlier runway evaluation and transfer, consistent with this policy.

The initial policy is included in this executable proposal, so no separate vote is needed to get started.
Any future changes to the funding policy require a social vote. This establishes clear expectations about responsibilities and decision-making: the treasury manager handles routine rebalancing within the policy, and the community sets the policy itself.

This proposal remains aligned with the IPS. The required three-year stablecoin runway is calculated at the Endowment level, including stablecoins held and deployed there, as is currently done. The expansion guidelines reference transferring 33% of protocol revenue to the Endowment. This proposal modifies that mechanism by routing revenue directly to the Endowment to improve operational efficiency and capital deployment.

Impact

The operational funding process is reactive, requiring a full governance cycle for each transfer. Meanwhile, capital that could be earning yield sits idle in controllers and the timelock for several months at a time.

With this proposal, capital flows from registrars to the endowment as soon as anyone calls withdraw(). The endowment begins generating yield immediately rather than after several months of governance overhead. Operational funding follows a clear quarterly process based on the two-year runway target, removing the need for ad-hoc proposals.

To put this in perspective: looking at the period from January 2024 until now, and considering ETH staking yields if this capital had been allocated to the endowment, a conservative estimate of $1 million in yield was left on the table. This is based on the balances held in the timelock, the current registrar controller, and the old registrar controller (which still have a relevant number of registrations). Going forward, every ETH collected will begin earning yield within days, compounding the DAO’s income over time.

Security

The Registrar Manager has a minimal attack surface. It only forwards funds to a configurable address and has no admin functions beyond governance-controlled registrar additions. The contract will be audited before deployment. If the snapshot proposal passes or there is high consensus, the Meta-Governance Working Group could fund the external audit.

Implementation

Everything ships in a single executable proposal:

a) Transfer ownership of all registrar controllers to the Registrar Manager, with the endowment set as the destination.

b) Transfer the ETH currently held in the timelock (~4,148 ETH) to the endowment.

c) Configure the Zodiac module on the endowment to allow the treasury manager to send ETH and USDC to the timelock.

Once executed, the entire flow is live. Karpatkey begins quarterly rebalancing. Future changes to the funding policy require a social vote.

Transactions

The calldata can be found on Tally draft.

The RegistrarManager contract and calldata verification test are open source, review and feedback is welcome.

I am in favor of solutions that may prevent funding lockups and extra proposals such as [EP 6.32] [Executable] Transfer $2.5M USDC from Endowment to wallet.ensdao.eth.

This proposal is a proactive step forward, @blockful.

Questions:

  1. Will the DAO ever have a reason to keep funds in the registrar temporarily, and if so, does permissionless withdraw() remove our ability to do that? (I’m concerned this could open possibility for griefing)
  2. What is the scenario where kpk would need to move the full endowment balance to the timelock without a governance vote? (I’m in strongly favor of allowances on the ZRM)
  3. With the proposed Registrar Manager inheriting ownership and control over the Registrar Controllers, what are the projected audit costs and who will perform?

-–

Quick thoughts on interaction with Endowment IPS:

  • The IPS states 33% of cash flows should be transferred to the endowment, this proposal routes all registrar revenue there.
  • Since registrar revenue is ETH and the endowment has a 60/40 target, effectively the total ENS treasury outside of the governance token would inherit that allocation rule.
1 Like

Strongly supportive of this.

@Coltron.eth to answer your first question, I can’t see any reason for the DAO to want to keep funds in the registrar controller, and this is already impossible as the ‘sweep’ function is callable by anyone in recent versions of the contract.

2 Likes

I’m assuming this is a temp check?

In favor.

Contract looks good. Small nitpicks:

  • Semantics. We are withdrawing from controllers not registrars - addRegistrar => addRegistrarController etc ?
  • registrar.code.length > 0 in addRegistrar() ?

Can the calldata be posted in this thread such that if/when the proposal is posted on chain it’s possible to validate that it was posted as proposed.


Tangentially related:

  • Is there a document that outlines all of the Zodiac roles in a clean/clear manner so it’s easy to see exactly what karpatkey can do?
  • Can karpatkey make the dashboard they present on Metagov calls a dynamic web interface that outlines realtime data. I’m mostly curious about the asset allocation and alignment with the IPS at any given time.

@Coltron.eth

2 Likes

Yes, we’re currently working on a live dashboard. We’ll make it available for the entire community once ready.

There’s no anticipated reason for the DAO to hold funds in the registrar, so a permissionless withdraw() shouldn’t pose any practical risk.

Under the proposed structure, this could only be triggered through a social vote, so it wouldn’t bypass governance.

We’ve reached out to a few auditors and got an estimate of around $12k. We’ll share more details on the selected auditor once that’s confirmed.

Great catch, we’ll update the naming accordingly. Thanks!

Correct, this serves as an extra safeguard to make sure only contracts can be added.

The calldata is already available via the link at the bottom alongside the tests, but we’ll also include it directly in the proposal for easier verification.

Yes, you can find the full breakdown here.

1 Like

Update: Ready for on-chain submission

The RegistrarManager contract has been audited by Cyfrin and deployed at 0x62627681D92e36b9aeE1D9A6BF181373ccd42552.

The calldata has been verified, succeeds in simulation, and matches the expected intent. We encourage delegates and contributors to review the calldata as well.

Tally draft: Tally | ENS Draft Proposal

Calldata review: calldataCheck.t.sol

The draft is ready for a delegate to submit on-chain.

To verify locally:

  1. git clone https://github.com/blockful/dao-proposals.git

  2. git checkout 565bfbf

  3. forge test --match-path "src/ens/proposals/ep-registrar-manager-endowment/*" -vv

Worth noting the following discrepancy:

The OP stated that the Registrar Manager contract would be deployed within a single executable proposal.

Yet:

The actual execution just calls addRegistrar on an existing address.

That does introduce a trust assumption before the vote passes (even with the audit trail).

Was a CREATE2-based in-proposal deployment considered, or was there a specific reason to go with pre-deploy?

What’s the trust assumption here? The contract can be verified and audited before voting, and the vote is what’s necessary to enable it. This is the standard pattern we always use, because deploying a contract inside an executable proposal bloats the proposal enormously.

According to the audit, Cyfrin signed off on e2f7584 — not the commit Blockful communicated the DAO should verify. That discrepancy is what prompted me to look more carefully.

The linked commit is actually a docs-only commit that adds the Cyfrin audit PDF (harmless).

Regardless, it was worth the time to inspect. We’re modifying the DAO’s treasury mechanism, not a small thing by any means.

Went ahead and compiled both commits independently and compared each against the deployed bytecode at 0x62627... via cast code. All three match.

Verification artifact → GitHub - estmcmxci/dao-audits: Independent bytecode verification artifacts for ENS DAO proposals · GitHub

Good to know for contributors. I was a bit apprehensive about the pattern of deploying from an EOA before a vote (that’s the paranoiac in me) and thought CREATE2 provides higher trust assurances.

Proposal text update @estmcmxci, so remains accurate! Thanks for pointing that out and comparing the bytecode as well. The more eyes, the better.

It’s better to deploy the contract before, so it’s simpler for delegates to review and in general it’s a more common practice than deploying in the proposal itself.

1 Like

Had another look through this code and executable calldata at the request of @netto.eth

Some thoughts.

1. Swallowed reverts

Significance: High

There are additional considerations associated with transferring ownership of the registrar controllers to the ETHRegistrarManager - the obvious one being that function definitions in the registrar controller contracts with the isOwner modifier now can only be executed by the new owner - ETHRegistrarManager.

In principle this is fine because the contract implements a passthrough execOnRegistrar which means that the timelock can still execute these functions through an executable proposal BUT as noted in the comments:

/// @dev Does NOT revert on call failure — the caller can inspect the returned `success`

Given that executable proposals are atomic in nature this means that a complex multistep proposal would not fail if an underlying controller call failed - this could lead to an inconsistent, unexpected state.

Recommendation

I think that execOnRegistrar should bubble reverts:

    if (!success) {
        assembly {
            revert(add(returndata, 32), mload(returndata))
        }
    }

2. Fully initialized contracts

Significance: Low

Following on from the discussion between @nick.eth and @estmcmxci I think we could go one step further and have the contract deployed and initialized up front. This would allow for simulations against fully initialized state.

Recommendation

  • Deploy ETHRegistrarManager
  • Call addRegistrar for each registrar controller
  • Transfer ownership to wallet.ensdao.eth

This would reduce the proposal complexity by removing 3 elements.

3. Semantically clear naming

Significance: Nit

The method names addRegistrar etc are not semantically clear. They are registrar controllers.

Recommendation

addRegistrarController etc?

  1. Zodiac

Significance: Needs confirmation

I don’t have an in depth knowledge of the Zodiac Roles Modifier, but one thing I noted was that the Zodiac calls within the executable are somewhat obfuscated by them being nested in execTransaction calls. As a curiosity I created:

I decomposed the nested calldata, and added some high level comments. One point stood out as being strange in relation to calldata 4.

That IS the USDC address.

That IS the USDC address.

That IS the selector for transfer(address,uint256).

That IS the address of wallet.ensdao.eth

That IS the address of wallet.ensdao.eth

:warning: The use of selector 0x00000000 for the ETH-send permission path is not self-evident to me from review alone. Is this the mechanism through which permission to send ETH is granted? Calldata generation code says as much, but it seems a little odd and I’m struggling to find documentaion.

Additionally, in all cases the role0x4d414e4147455200000000000000000000000000000000000000000000000000 matches that listed here: https://roles.gnosisguild.org/eth:0x703806E61847984346d2D7DDd853049627e50A40/roles/MANAGER

Recommendation

Verify calldata 4 is the correct approach. It possibly/probably is, I just don’t have enough knowledge of the Zodiac codebase and I can’t find documentation.

3 Likes

Seems like a clear win for operational efficiency with alignment between folks who chimed in here.

I’m happy to submit this proposal onchain for the DAO, however seems that @clowes.eth brought up some technical points for consideration. Per enswallets.xyz there’s only 300k left.

I’d encourage @blockful & @clowes.eth to align quickly.

The version deployed was the one audited by Cyfrin. We have a PR which addresses the comments from Clowes but we need proper check with Cyfrin.

We recommend proceeding with the proposal as it’s, due to already being well tested, audited and to solve the situation that Slobo pointed out.

Regarding thoughts that Clowes raise:

  1. Swallowed reverts - On calldata review, we do check state changes. In a future version we can update the deployed version to better address this concern. We don’t consider it as a blocker.
  2. Contract already deployed and with timelock as owner. The proposal itself includes the addition of the Registrar Controllers.
  3. Acknowledged and let’s address the naming in the next version!
  4. Double checked via foundry tests.

Thanks again for the review.

1 Like

Live proposal calldata security verification

This proposal is live.

Calldata executed the expected outcome. The simulation and tests of the live proposal can be found here.

To verify locally:

  1. Clone: git clone https://github.com/blockful/dao-proposals.git
  2. Checkout: git checkout 9c71625
  3. Run: forge test --match-path "src/ens/proposals/ep-6-39/*" -vv

Respectfully, I think this is a bad call - issue one is a real, and significant issue.

There is a certain irony to a proposal that seeks to optimize and improve ENS governance simultaneously creating a regression as regards the atomic nature of proposal execution.

This effectively replaces a deterministic safety mechanism with human review. It also creates a dependency on yourselves..

We have examples of this exact class of issue happening in the past:

  • I missed this exact category of problem in the original on.eth proposal (reference).

  • You yourselves missed this exact category of problem in the calldata review of the original on.eth proposal (reference).

Given that this proposal moves us toward more automation and fewer governance checkpoints, silent partial failures are potentially incredibly dangerous. Yes, the surface area for something going wrong here is small but we should be setting high standards for ourselves. How do we expect large and serious players to depend on the ENS protocol and take us seriously if we take a ‘meh, we can fix it later’ attitude to basic software delivery?

The fix here is minimal (a few lines of code to bubble reverts), and preserves the atomic guarantees that ENS governance currently relies on.

I agree this proposal is important and worth moving forward promptly but I don’t think we should knowingly introduce a regression in safety when it’s this straightforward to avoid.

It is also concerning to me that Cyfrin missed this in their audit.

1 Like

Welcome, wish I knew about https://sourcify.dev before spending a couple of hours learning how to and then comparing the bytecode myself :sob:

Should we opt-in to this? @PublicGoods_Stewards funded this via Argot Collective — seems like a good step to add in the hygiene process when submitting executables to the DAO.

I agree that this is a real problem that should be fixed. Rather than create confusion and delay by trying to vote down the proposal at this point, I’d suggest letting this vote succeed and then aiming to post a fixed contract, and migrate over to it, as soon as possible afterwards.

4 Likes

I think it’s worth noting that, before this went to a vote, two contributors flagged material implementation risks that were largely overlooked.

A “ship now, fix later” approach isn’t sustainable. Delegate attention is scarce and costly, and this turns one decision into multiple follow-on decisions that repeatedly consume bandwidth.

Maybe this doesn’t bite as hard today, while voting power remains concentrated. But if we’re serious about decentralization and a broader delegate set, requiring case-by-case votes for routine technical fixes becomes a significant governance drag.

If we care about efficiency, we should avoid normalizing non-atomic execution paths that predictably require follow-up votes (the current process of claiming TLD ENS nodes is one example).

Instead, we should formalize optimistic processes for routine technical updates: bounded scope, transparent diffing, a clear challenge window, and immediate escalation to full governance if contested.

Execution can then be delegated to a trusted technical body operating within that bounded process.

That way, governance is reserved for where legitimacy is required, and optimistic processes handle implementation hygiene.

I agree that this should not have gone live without fixing the identified issues. But in view of the fact that it has, and given that vigilance is enough to avoid these issues having a material effect, the best path forward would seem to be fixing them with a revised implementation, rather than trying to reverse what has already been done. Obviously if the identified issue was critical and could be exploited externally, my view would be different.

1 Like