OpenZeppelin / OpenZeppelin/ui-builder
feat(builder): configurable ENS address preview mode (rich vs simple) for forms
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 48
- Forks
- 23
- Avg merge
- 3h 1m
- Merged PRs (30d)
- 10
Description
Summary
Form preview and exported apps currently render blockchain-address fields via TransactionForm → DynamicFormField → bare AddressField, which shows the simple ENS feedback UX (inline “Resolved to 0x…” announcer + optional cross-network disclaimer).
Elsewhere in the product we already ship a rich ENS preview (avatar + reverse-ENS card) using Pattern A:
AddressFieldWithResolvedPreview(@openzeppelin/ui-components)ResolvedAddressFieldPreviewWithNameResolution(@openzeppelin/ui-renderer)- parent
useWatchforpreviewAddress
Examples: Address Book Add Alias dialog, builder EOA config / hardcoded address surfaces (BlockchainAddressFieldWithRichPreview).
Goal: let form builders choose the ENS address preview style for function input fields in preview + exported apps, without forking TransactionForm per app.
| Mode | UX | Default for |
|---|---|---|
rich (new default for new forms) |
Pattern A preview card below field; suppresses forward success announcer | New Contract UIs |
simple (current behavior) |
Inline “Resolved to 0x…” text announcer |
Existing saved/exported forms (backward compat) |
Problem
fieldRegistry in @openzeppelin/ui-renderer maps blockchain-address → AddressField only. Builder-specific wrappers (BlockchainAddressFieldWithRichPreview) are not used by:
FormPreview.tsx(TransactionForm)- Exported
GeneratedForm(form-component.template.tsx→TransactionForm)
So preview/export diverge from the richer UX users see in Address Book.
Design principles (constitution-aligned)
Per UI Builder Constitution — Principle I: chain-agnostic core, adapter-led architecture.
✅ DO: capability-led detection
Gate features on runtime capabilities, not ecosystem strings:
// Forward name resolution available?
Boolean(runtime?.nameResolution?.resolveName)
// Reverse name resolution available? (rich preview card)
Boolean(runtime?.nameResolution?.resolveAddress)
This matches existing SF-3 wiring (useRuntimeNameResolver → activeRuntime?.nameResolution) and NameResolutionCapability structural feature detection in @openzeppelin/ui-types.
When a chain has no name-resolution service, the adapter omits nameResolution from EcosystemRuntime — UI degrades gracefully (hex-only address input, empty resolver → UNSUPPORTED_NETWORK for names).
❌ DON'T: ecosystem conditionals
Do not use networkConfig.ecosystem === 'evm' (or similar) to show/hide the toggle or pick preview mode. That duplicates adapter knowledge and violates Principle I.
Proposed architecture
Builder UI toggle (General Settings)
↓
BuilderFormConfig.ensAddressPreview: 'rich' | 'simple'
↓
FormSchemaFactory.builderConfigToRenderSchema()
↓
RenderFormSchema.ensAddressPreview (embedded in export @@FORM_SCHEMA_JSON@@)
↓
TransactionForm reads schema
↓
DynamicFormField (blockchain-address)
├─ 'rich' → AddressFieldWithResolvedPreview + ResolvedAddressFieldPreviewWithNameResolution + useWatch
└─ 'simple' → AddressField (current)
Single implementation path in @openzeppelin/ui-renderer so builder preview, export, and any direct TransactionForm consumer stay aligned.
Scope
In scope
- Form-level setting persisted in
BuilderFormConfig/RenderFormSchema - Builder UI control (General Settings or dedicated “ENS display” section)
-
TransactionForm+DynamicFormFieldrich/simple rendering forblockchain-address - Nested address fields (object / array / map) inherit form-level mode
- Export codegen carries setting in serialized form schema (no template fork)
- Backward compatibility for existing Contract UI records / exports without the field
- Capability-led UI visibility (show toggle only when forward name resolution is available)
Out of scope (this issue)
- Per-field override (form-level only for v1)
- Changing Address Book Add Alias (already rich + network-scoped internally in ui-renderer 3.4+)
- EOA execution config / contract-definition builder surfaces (already use
BlockchainAddressFieldWithRichPreview; optional follow-up: dedupe via shared renderer component) - New name-resolution protocols beyond what adapters expose today
Implementation plan
Phase 1 — @openzeppelin/ui-types + @openzeppelin/ui-renderer (upstream)
Tracking: open a companion issue/PR in openzeppelin-ui (blocks builder integration).
-
Add typed setting (prefer first-class field over
metadata):export type EnsAddressPreviewMode = 'rich' | 'simple'; // On CommonFormProperties or RenderFormSchema: ensAddressPreview?: EnsAddressPreviewMode; -
Renderer field component — e.g.
BlockchainAddressDynamicField:useWatch→previewAddressrich: Pattern A (AddressFieldWithResolvedPreview+ResolvedAddressFieldPreviewWithNameResolution,networkIdfromadapter.networkConfig.id)simple: bareAddressField- Thread mode via context from
TransactionFormthrough recursiveDynamicFormFieldcalls
-
Defaults in renderer:
ensAddressPreviewomitted →'simple'(preserves existing exported apps)- explicit
'rich'/'simple'honored
-
Degradation (no throws):
- No
nameResolutioncapability → both modes behave as plain address field (existing SF-3 empty-resolver behavior) richwithoutresolveAddress→ forward resolution still works; preview card may show address-only
- No
-
Tests:
DynamicFormField/TransactionFormfor both modes; nested fields; capability-absent runtime
Dependency floors (when shipping): align with network-scoped ENS stack already adopted in builder PR #401 (ui-renderer ^3.4.0, ui-components ^3.8.0, ui-react ^3.3.0).
Phase 2 — ui-builder (this repo)
- Extend
BuilderFormConfigwithensAddressPreview?: EnsAddressPreviewMode(defaultrichfor newly created forms). FormSchemaFactory.builderConfigToRenderSchemaalready spreads builder config → schema; verify field is included in export JSON.- Builder UI — radio in General Settings:
- Rich preview — “Show avatar and reverse-ENS card below address fields”
- Simple — “Show inline ‘Resolved to 0x…’ text”
- Visible only when
Boolean(runtime?.nameResolution?.resolveName)(capability-led, not ecosystem)
- Migration: existing
ContractUIRecord.formConfigwithoutensAddressPreview→ render as'simple'(renderer default); do not auto-migrate DB on read. - Tests:
EnsExportPins, export snapshot tests, form schema factory unit tests. - Optional cleanup: reimplement
BlockchainAddressFieldWithRichPreviewas thin wrapper around upstreamBlockchainAddressDynamicFieldto avoid duplicate Pattern A wiring.
Related work already merged/in flight: PR #401 (ENS featureset, rich preview on builder-only surfaces, export ENS wiring).
Builder UI mock (copy)
ENS address preview
Choose how address fields display ENS resolution feedback.
- Rich preview (recommended) — avatar + name card below the field
- Simple — inline “Resolved to 0x…” text
Only shown when the active network runtime exposes forward name resolution (
nameResolution.resolveName).
Acceptance criteria
- New form defaults to rich preview in builder; toggling updates live Form Preview immediately
- Exported app ZIP contains
ensAddressPreviewin embedded form schema JSON - Exported app rich mode matches builder preview (same
TransactionFormpath) - Existing saved Contract UIs without the field keep simple announcer (no visual regression)
- Stellar / other chains without
nameResolutioncapability: toggle hidden; address fields work as today - No
ecosystem === 'evm'(or similar) conditionals in builder or renderer for this feature - ENS forward + reverse + mainnet L1 fallback (003 opt-in) still work in both modes where capability exists
- Unit/integration tests green in ui-renderer and ui-builder
References
Canonical Pattern A (openzeppelin-ui):
examples/basic-react-app/src/components/AddressFieldDemo.tsxexamples/basic-react-app/src/components/ENSResolutionDemo.tsxpackages/renderer/src/components/AddressBookWidget/AddAliasDialog.tsxpackages/renderer/src/components/ResolvedAddressFieldPreviewWithNameResolution.tsxpackages/react/src/hooks/nameResolution/useRuntimeNameResolver.ts
ui-builder today:
apps/builder/src/components/UIBuilder/StepFormCustomization/FormPreview.tsx→TransactionFormapps/builder/src/components/fields/BlockchainAddressFieldWithRichPreview.tsxapps/builder/src/core/factories/FormSchemaFactory.tsapps/builder/src/export/codeTemplates/form-component.template.tsx
Types:
NameResolutionCapability—@openzeppelin/ui-types(resolveName?,resolveAddress?, structural feature detection)
Suggested labels / milestone
- Labels:
enhancement,feature,evm(product area only if desired — not for runtime gating) - Milestone: post–PR #401 ENS rollout
- Blocks: upstream ui-renderer issue (create linked issue in openzeppelin-ui)
Open questions
- Should rich require both
resolveNameandresolveAddress, or is forward-only + address card acceptable when reverse is missing? - Per-field override in a later version — worth reserving schema shape (
field.metadata.ensAddressPreview) or keep form-level only? - Should builder EOA / contract-definition surfaces respect the same form-level setting, or stay always-rich?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with apps/builder/src/components/UIBuilder/StepFormCustomization/FormPreview.tsx, apps/builder/src/core/factories/FormSchemaFactory.ts, and apps/builder/src/export/codeTemplates/form-component.template.tsx, then review the upstream ui-renderer dependency and its DynamicFormField/TransactionForm tests. Trace how form configuration reaches the embedded schema and preview, including nested address fields and capability detection. Done means the setting is persisted, preview and exports match, existing forms remain simple, and the named export and schema tests pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- developer-experience, frontend, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100