ADR-006: Pricing model and controller design
- Status: Accepted
- Date: 2026-04-22
- Deciders: Core team
Context
Oniym's economic and operational model requires decisions on four axes:
- Pricing tiers — flat-rate vs length-based premiums (ENS-style)
- Emergency controls — should the controller be pausable?
- Registration atomicity — single tx or multi-tx setup?
- Reverse resolution — opt-in at registration, or post-registration only?
Each choice affects user experience, security posture, and protocol positioning.
Decisions
1. Two-tier pricing for all registrable lengths
- Minimum length: 3 characters
- Monthly rate: $3.00 per 30 days — for registrations under 1 year
- Annual rate: $15.00 per year — for registrations of 1 year or more (saves $21 vs 12× monthly)
- All prices paid in ETH at Chainlink spot, regardless of name length
- Billed by duration in seconds — users can register for any duration
- No post-expiry premium decay (future work)
2. OpenZeppelin Pausable on ETHRegistrarController only
commit(),register(),renew()are pausableRegistryandBaseRegistrarare NOT pausable- Pause controlled by protocol owner (multisig initially, DAO later)
3. Multicall resolver data in registration
register()acceptsbytes[] resolverDatato batch setAddr/setText calls- Empty array → no resolver setup (user handles later)
- Non-empty → controller loops and calls resolver atomically
4. Opt-in reverse record at registration
register()acceptsbool reverseRecord- If true, controller sets reverse record via
IReverseRegistrar.setNameForAddr - If false, user can set reverse record later (or not at all)
Rationale
Two-tier pricing
Positioning: ENS charges $5/$160/$640 for 5+/4/3-char names. Oniym's $3/month and $15/year is a clear differentiator — "the affordable multichain alternative." This matters for Base's price-sensitive user base and emerging-market targeting.
Why two tiers: A single flat annual rate forces users to commit $15 upfront to try the protocol. A $3/month entry point lowers the trial barrier significantly. Users who commit annually save $21 vs paying monthly — rewarding loyalty without penalizing newcomers.
Squatting reality: Short names will be squatted on launch regardless of tier. Premium pricing only shifts who squats (capitalized squatters) vs when they squat. Accept the outcome and move on.
Revenue expectations: With 1000 annual registrations at $15/year, annual revenue is ~$15K. With mixed monthly/annual registrations the blended rate is higher. Pricing is intentionally accessible to maximize adoption over revenue.
Future flexibility: The IPriceOracle interface preserves the Price struct with base + premium. Owner can update monthlyPriceUsd and annualPriceUsd at any time via setMonthlyPrice() and setAnnualPrice() without redeploying the controller.
Pausable on controller
Defense layering: Combined with removeController() on the registrar, this gives three response modes for bugs:
| Severity | Response | Time |
|---|---|---|
| Bug in controller logic | Pause, fix, unpause | hours |
| Severe bug in controller | removeController(), deploy new | ~1 day |
| Bug in ownership layer | No recovery — immutable | — |
Why not pause Registry/Registrar: The ownership layer must remain functional even during incidents. Users must always be able to transfer or resolve their names. Pausing ownership = bricking user assets temporarily, which is unacceptable.
Governance constraint: The pause power is a centralization trade-off. Mitigation: multisig with timelock on the owner role, visible pause events, published criteria for when pause will be used.
Multicall resolver data
UX impact: Without it, registration is a 6-tx flow:
commit()register()setAddr()for ETHsetAddr()for SolanasetAddr()for BitcoinsetText()for avatar
With it, steps 3-6 collapse into step 2. User experiences it as "register and I'm done." For a naming protocol, this is a major UX win.
Gas cost: ~5-10K extra gas per extra call (mostly calldata). Total registration cost stays under 250K gas on Base, still well under $0.50 at typical gas prices.
Security consideration: The controller performs bytes multicall to the resolver. Implementation MUST validate that the resolver address matches the one set in the Registry to prevent cross-contract privilege escalation. Documented in threat model.
Opt-in reverse record at registration
Why not default-true: Users on shared wallets (team multisigs, smart accounts) may not want the wallet address to resolve to a personal name. Opt-in respects user choice.
Why not default-false: The large majority of users want reverse resolution and won't know to set it manually later. Exposing as a flag makes it discoverable and one-click.
ENS precedent: ENS registrar v0.3+ uses this same pattern. Copying proven UX is valuable.
Consequences
Positive
- Simpler pricing logic reduces oracle attack surface
- Pausable is industry-standard and reviewer-friendly
- Atomic registration is competitive with ENS's latest UX
- Opt-in reverse respects user autonomy
Negative
- Flat pricing leaves premium revenue on the table for short names
- Pausable introduces centralization surface (addressed via timelock)
- Multicall increases complexity slightly and requires careful validation
- Reverse record requires deploying
ReverseRegistrarand wiring controller authorization (additional scope)
Mitigations
- Pause power ownership transitions to multisig before mainnet, DAO after
- Reserve ability to migrate to tiered pricing post-launch (oracle swap)
- Multicall validation is unit-tested with malicious resolver inputs
- Reverse record flag defaults to false if resolver is not set

