ERC-8017: Payout Race
Minimal ERC for a single-asset payout bucket that vends its entire balance for a fixed payment amount.
Abstract
This ERC specifies a small contract surface for a "payout race": a bucket that holds a single payout asset type and transfers the entire bucket to a recipient when a caller pays a fixed required payment in a configured desired payment asset. The desired payment asset can be ETH or one ERC-20. The payout asset can be ETH or one ERC-20.
This ERC is inspired by the Uniswap Foundation's Unistaker proposal, which introduced the term Payout Race and motivated this design.
Motivation
Many protocols need an ongoing way to convert a continuous stream of value into another asset at or near prevailing market prices. Typical cases include buying back a protocol token using protocol revenue, accumulating a reserve asset, funding incentive budgets, or rebalancing treasuries. Existing patterns have material drawbacks. Integrating an AMM couples outcomes to external liquidity, slippage, and fees, and requires retuning when pool conditions change. General on-chain auctions add operational complexity and higher gas, especially when run continuously.
This ERC defines a deterministic, revenue-driven primitive that is analogous to a Dutch auction. Sources of value flow into this contract, filling a "bucket" of purchasable assets. The first caller that supplies the required payment in the desired payment asset receives the entire current balance of the payout token in the bucket. The interface is small, auditable, and easy to compose with upstream controllers that decide when the exchange is economically sound.
Specification
The following interface and rules are normative. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Definitions
-
Conforming contract: Any smart contract that exposes this interface and claims compliance with this ERC. This includes proxies and clones. Requirements in this document apply to the observable runtime behavior of the deployed contract.
-
Payout asset: Asset dispensed from the bucket.
payoutAsset == address(0)means ETH payout. -
Desired payment asset: Asset the buyer must pay. Referred to as
desiredAssetin the interface.desiredAsset == address(0)means ETH payment. -
Required payment: Fixed amount of the desired payment asset (
desiredAsset) or ETH that must be provided by the buyer to trigger the payout.
Interface
Required Behavior
-
Exact required payment. Callers MUST provide exactly
requiredPayment()in the configureddesiredAssetor in ETH to callpurchase. -
Token pairing.
payoutAssetanddesiredAssetMUST NOT both beaddress(0). ETH on both sides is disallowed. -
Desired payment asset immutability.
desiredAssetMUST NOT change after initialization. A conforming contract MUST NOT expose any callable setter that can changedesiredAsset. -
Payout asset immutability.
payoutAssetMUST NOT change after initialization. A conforming contract MUST NOT expose any callable setter that can changepayoutAsset. -
All-or-nothing dispense. On
purchase, a conforming contract MUST compute the amount to dispense as the live balance of the payout token captured at function entry, before any external calls. The contract MUST transfer exactly this amount totoin a single call and the call MUST revert if this amount is zero. -
Payment collection.
- If
desiredAsset == address(0),purchaseMUST requiremsg.value == requiredPayment()and MUST forward that ETH topaymentSink(). - If
desiredAsset != address(0),purchaseMUST requiremsg.value == 0and MUST calltransferFrom(msg.sender, paymentSink(), requiredPayment())ondesiredAsset.
- If
-
Admin changes. A conforming contract MUST restrict the admin setters to an authorized role and MUST emit
PaymentConfigUpdatedwhenrequiredPaymentorpaymentSinkchange.
Optional Extensions
- Permit for payment: A conforming contract MAY expose
purchaseWithPermit(...)that accepts EIP-2612 permit parameters. If implemented, the function MUST requiredesiredAsset != address(0), MUST callpermitondesiredAssetwith the supplied signature, and MUST collectrequiredPaymentviatransferFromin the same transaction. The call MUST revert ifdesiredAssetdoes not implement EIP-2612 or if the permit does not yield sufficient allowance. - Rescue for unintended assets: A conforming contract MAY implement an admin-only
rescuefunction to recover assets that are not thepayoutAsset(e.g., unsolicited ERC-20s or ETH sent whenpayoutAssetis an ERC-20). If provided, the function MUST NOT transfer thepayoutAsset, MUST emit aRescued(address token, address to, uint256 amount)event, and MUST be restricted to an authorized role.
Rationale
- A single required payment pairs well with controllers that evaluate when the exchange is economically sound and trigger
purchaseonly when conditions justify it. The onchain primitive then validates the payment and atomically transfers the entire bucket. paymentSinkreduces persistent balances in the contract and simplifies audits. Sinks can be treasuries, splitters, or burns.- Using the live onchain balance as the source of truth automatically captures rebases and fee-on-transfer mechanics, and keeps the onchain tracking minimized. It also implies that unsolicited transfers to the contract will be included in the next payout, which purchasers may want to account for at the integration level.
Admin Considerations
Access control for admin setters is intentionally unspecified; EIP-173 ownership or a role-based pattern is recommended.
Some deployments may renounce or restrict admin rights for policy or compliance reasons (for example, renouncing ownership or disabling roles). This ERC does not prescribe any specific mechanism.
The reference uses EIP-173 style ownership for illustration. Any access control that enforces the Required behavior is acceptable. Deployments may assign distinct roles per setter or make one or more parameters immutable. The specification is agnostic to the mechanism.
Parameter Selection and Degenerate Cases
This mechanism works best when value accrues gradually. Large, lumpy deposits can overshoot the required payment threshold and leak value to the first successful caller. Operators should size requiredPayment relative to observed inflow volatility and adjust conservatively. If the payout asset appreciates against the desired payment asset, purchases may stall. If it depreciates, purchases may trigger so frequently that value is lost whenever a large trade pushes the bucket well above the threshold.
Changing requiredPayment carries risks. Lowering it can leak value at the moment of change if accrued payout already exceeds the new threshold, since searchers can win a bargain. Raising it can disrupt or bankrupt naive searchers and MEV bots that provide rewards by arbitraging fee collection. Mitigations may include timelocked or scheduled parameter changes, announce windows, caps on per-block deposits, cooldowns after changes, and time-weighted average pricing (TWAP)-based or ratcheted adjustments to requiredPayment.
Considered Alternatives: Multi-Asset Sweep
This design could be extended to support multiple payout assets by maintaining an explicit allowlist and, on a successful purchase, sweeping each allowlisted token to the recipient using the same mechanics as the single-asset case.
Backwards Compatibility
Compatible with any ERC-20. Wallets and dApps can integrate using standard allowance flows or optional permit helpers.
Reference Implementation
Security Considerations
-
Payout accounting. The dispensed amount is computed from the live onchain balance of the payout asset. Because ETH-to-ETH is disallowed, there is no ambiguity about subtracting
msg.value. Capture the amount to dispense at function entry and use that value for the transfer. -
Reentrancy and external calls. Use the Checks-Effects-Interactions pattern and a reentrancy guard. Avoid any external calls before you (a) capture the amount to dispense and (b) forward payment to
paymentSink. Do not perform callbacks between collecting payment and completing the payout. -
Receiver constraints. The recipient
tomust be able to receive the asset being dispensed. ETH payouts require a payable fallback; ERC-20 payouts require thattois not a contract that reverts ontransfer. -
Payment sink constraints. The
paymentSinkmust be able to receive the desired payment asset. For ETH payments,paymentSinkmust be payable. For ERC-20 payments,paymentSinkmust not revert when credited viatransferFrom. Using a burn address, splitter, or treasury is acceptable; the specification is agnostic to the mechanism. -
Unsolicited transfers. The next payout will include any assets pushed to the contract (e.g., direct ETH sends or ERC-20 transfers). Operators should account for this at the integration layer, or front the contract with filters if needed. An optional admin-only
rescuefor non-payoutAssetassets can mitigate mistakes without affecting conformance. -
Approvals and permits. When using ERC-20 payments, callers should consider allowance race conditions. If a
purchaseWithPermithelper is implemented, verify domain separator, deadline, and nonce handling, and revert on insufficient post‑permit allowance. -
Admin changes. Because setters can change
requiredPaymentorpaymentSink, governance should protect these operations. Common mitigations include timelocks, scheduled changes with announcement windows, and immutability for parameters that should never change. -
Proxies and clones. Constructors do not run per proxy or minimal clone. Implementations should set
payoutAssetanddesiredAssetonce during initialization and ensure they cannot change afterward. Avoid exposing setters and protect initializers against re-entry or multiple calls.
Copyright
Copyright and related rights waived via CC0.