OpenZeppelin / OpenZeppelin/ui-builder

feat(builder): configurable ENS address preview mode (rich vs simple) for forms

Open
#402 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
48
Forks
23
Avg merge
3h 1m
Merged PRs (30d)
10

Description

Summary

Form preview and exported apps currently use TransactionFormDynamicFormField → bare AddressField, which shows the simple ENS forward-resolution UX (inline “Resolved to 0x…” text + optional cross-network disclaimer — see screenshot in PR #401 discussion).

Builder-specific surfaces already use Pattern A (rich preview card with avatar via AddressFieldWithResolvedPreview + ResolvedAddressFieldPreviewWithNameResolution + useWatch), e.g.:

  • apps/builder/src/components/fields/BlockchainAddressFieldWithRichPreview.tsx
  • Address book Add Alias (internal to AddressBookWidget / AddAliasDialog in ui-renderer ≥3.4.0)

Gap: end-user transaction forms (preview + export) never take the rich path because packages/renderer/src/components/fieldRegistry.ts maps blockchain-addressAddressField.

We want a form-level builder setting so users can choose:

Mode UX
rich (new default for new forms) Pattern A preview card (name + avatar) below address inputs
simple (current behavior; preserve for existing exports) Inline “Resolved to 0x…” announcer

Motivation

  • Parity between builder preview, exported apps, and rich ENS surfaces (address book, EOA config)
  • User control over visual density / branding
  • Safe rollout: existing saved/exported forms should not change appearance unless opted in

Architecture (constitution-compliant)

Per .specify/memory/constitution.md Principle I (chain-agnostic, adapter-led):

  • Do NOT gate on ecosystem === 'evm' or chain id strings
  • Do use runtime capabilities: runtime?.nameResolution (NameResolutionCapability)
  • Feature detection is structural per ui-types: capability.resolveName, capability.resolveAddress
  • When capability is absent, components degrade gracefully (existing SF-3 behavior via empty NameResolver)
Data flow
Builder UI toggle (General Settings)
    ↓
BuilderFormConfig.ensAddressPreview: 'rich' | 'simple'
    ↓
FormSchemaFactory.builderConfigToRenderSchema()
    ↓
RenderFormSchema.ensAddressPreview  (serialized into export @@FORM_SCHEMA_JSON@@)
    ↓
TransactionForm reads schema → DynamicFormField → blockchain-address field
    ↓
'rich'  → AddressFieldWithResolvedPreview + ResolvedAddressFieldPreviewWithNameResolution
'simple' → AddressField (current)
Capability-led UI gating (builder only)

Show the setting when the active runtime supports forward name resolution:

const canResolveNames = Boolean(runtime?.nameResolution?.resolveName);

Optional: only advertise rich when resolveAddress is also present; otherwise rich degrades to address-only card.

Hide control when !canResolveNames — because the adapter didn’t publish name resolution, not because of ecosystem string checks.


Upstream dependency (openzeppelin-ui)

This feature requires renderer support — builder-only wrappers will not fix exported apps.

Phase 1 — @openzeppelin/ui-types

Add typed setting (prefer first-class field over metadata):

export type EnsAddressPreviewMode = 'rich' | 'simple';

// On CommonFormProperties or RenderFormSchema:
ensAddressPreview?: EnsAddressPreviewMode;
Phase 2 — @openzeppelin/ui-renderer
  • Implement BlockchainAddressDynamicField (or equivalent) with useWatch + mode switch
  • Wire from DynamicFormField when field.type === 'blockchain-address'
  • Thread mode through nested DynamicFormField recursion (object/array/map children)
  • TransactionForm reads schema.ensAddressPreview and provides context
  • Defaults:
    • undefined / omitted → 'simple' (backward compat for existing exports)
    • explicit 'rich' → Pattern A
    • explicit 'simple' → current announcer

Reference implementations:

  • openzeppelin-ui/examples/basic-react-app/src/components/AddressFieldDemo.tsx
  • openzeppelin-ui/packages/renderer/src/components/AddressBookWidget/AddAliasDialog.tsx
  • openzeppelin-ui/packages/renderer/src/components/ResolvedAddressFieldPreviewWithNameResolution.tsx
Phase 3 — ui-builder (this repo)
  • Extend BuilderFormConfig / persist via ContractUIRecord.formConfig
  • UI: radio in General Settings (or ENS subsection) — Rich vs Simple
  • Default new forms to 'rich'; treat missing field as 'simple' for loaded records
  • FormPreview.tsx — no structural change once renderer supports schema field
  • Export — schema JSON already embedded via form-component.template.tsx; update EnsExportPins.verification.test.ts / snapshots
  • Consolidate BlockchainAddressFieldWithRichPreview.tsx to re-use upstream field component once available
  • Bump ui-renderer floor when upstream ships

Out of scope / anti-patterns

  • ❌ Forking TransactionForm only in builder (export diverges)
  • ❌ Patching fieldRegistry only in export templates
  • ❌ Defaulting existing schemas to 'rich' without migration
  • if (ecosystem === 'evm') conditionals anywhere in UI

Acceptance criteria

  • Builder toggle visible only when runtime.nameResolution?.resolveName is present
  • Form preview reflects selected mode for blockchain-address fields
  • Exported app formSchema includes ensAddressPreview and matches preview
  • Existing Contract UI records without the field render as simple (unchanged)
  • New forms default to rich when name resolution is available
  • Non-capable networks: address fields work; rich mode degrades safely
  • ENS 003 mainnet L1 fallback: simple mode keeps announcer + disclaimer; rich mode uses preview card
  • Tests: renderer unit tests (both modes), builder export pin/snapshot tests, manual QA on Sepolia + pasevin.eth

Related work

  • PR #401 — ENS featureset + rich preview on builder-specific surfaces (feat/ens-mainnet-l1-fallback-003)
  • openzeppelin-ui PR #195 — network-scoped ENS resolution (ui-renderer 3.4.0)
  • Pattern A reference: BlockchainAddressFieldWithRichPreview.tsx, AddressBookDialog.tsx

Suggested sequencing

  1. openzeppelin-ui issue/PR: types + renderer implementation + tests
  2. ui-builder follow-up on PR #401 (or stacked PR): UI toggle, schema plumbing, floor bump, snapshots

Labels

enhancement, ens, export, ui-renderer-dependency

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the data flow in this issue, .specify/memory/constitution.md, and the named builder files, then review the upstream ui-types and ui-renderer work required before changing ui-builder. Trace BuilderFormConfig through FormSchemaFactory.builderConfigToRenderSchema(), FormPreview.tsx, and form-component.template.tsx. Done means the toggle, serialized schema, preview, export behavior, compatibility defaults, and listed tests agree across supported runtimes.

Written by the indexing model from the issue text.

Assessment

Tech stack
blockchain, react, typescript
Domain
blockchain, frontend, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.