[RFC] Introducing a Subsidy Contract

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

  1. 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.
  2. A 24–48h dispute window opens — anyone can verify and challenge
  3. 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 RegistrarManager on withdrawAll()
  • 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.

  1. 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?
  2. 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?
  3. 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.