Circling back to this with a more concrete design. The OP left the accounting mechanism open-ended intentionally — the conversation surfaced why per-user credit balance tracking is a naive approach.
Regarding the accounting mechanism: we need a way to avoid the state-write problem entirely to eliminate overhead, and a way to require no user action for seamless, universal UX.
I propose a novel approach — a retroactive gas rebate via epoch push:
How it works
- A publisher posts a Merkle root onchain by submitting a bond — anyone can do this. The computation is deterministic from public onchain event data, so any actor can independently verify or produce the correct root.
- A 24–48h dispute window opens — anyone can verify and challenge
- ETH is pushed automatically to eligible addresses
No claim step. No wallet requirement. Works for EOAs, smart accounts, multisigs — the vast majority of address types.
The only failure case is contracts that explicitly reject ETH (no receive(), or a receive() that reverts) — an edge case mostly limited to protocol treasuries and some proxy contracts.
Failed pushes don’t lose the rebate: it stays in the subsidy contract and is claimable directly by that address before the epoch expires. Push is the default; claim is the fallback.
The UX: you used ENS, ETH arrived in your wallet — contingent on a root being published each epoch. In practice this is a low-maintenance operation; the bond mechanism ensures anyone can step in.
The epoch delay is addressed by a real-time accrual dashboard — users see their pending rebate growing as they interact with ENS throughout the epoch. The delay becomes engagement, not friction.
—
Funding the subsidy contract
Originally, I’d intended to modify the Registrar Controller directly to split funds between the Treasury and the proposed Subsidy Contract. @Blockful’s Treasury Flow Automation (TFA) proposal changes that — and improves on it.
The epoch push rebate mechanism is downstream of TFA. Because TFA introduces the RegistrarManager as the single owner of all controllers — with a permissionless withdrawAll() that routes ETH to a DAO-configurable destination — we can insert a RevenueSplitter contract at that destination rather than touching the controllers at all.
The RevenueSplitter would be set as the RegistrarManager’s destination via a single setDestination() call, bundled into the same executable proposal that deploys both contracts.
Its properties:
- Receives ETH from
RegistrarManageronwithdrawAll() - Splits by a DAO-configured ratio (e.g. 95/5) — set on deployment, adjustable by timelock
- Routes the majority to the Endowment (
endowment.ensdao.eth) - Routes the minority to the Subsidy Contract
- Zero changes required to blockful’s contracts
- Simple, auditable — ~50 lines of Solidity
—
Design Questions:
Building on Nick’s framing — rebates capped at 10% of a user’s registration fees, with expiry — the open design questions are around the mechanism itself.
- Bond sizing and slashing: the publisher bond needs to exceed the maximum epoch rebate to make griefing unprofitable. What’s the right floor, and who captures a slashed bond — the challenger, or does it burn?
- Dispute mechanism: “anyone can challenge” needs to be deterministic in practice. The standard pattern is: challenger submits a leaf, proof, and expected value that contradicts the root, and the contract verifies and slashes. Does this feel sufficient, or is there a simpler construction?
- Batched push execution — if an epoch has thousands of eligible addresses, a single push transaction will exceed the block gas limit. Push execution would need to be batched across multiple transactions. Worth designing for from the start; at current gas levels the execution cost is negligible.
—
If this reasoning sounds right, I’ll follow up with a more detailed spec for community feedback on the design — and if there’s rough consensus, move to a Temp Check.