Skip to content

STA-19: proposal-first ENS trait + subdomain management#50

Merged
gertsio merged 5 commits intomainfrom
agent/STA-19-manage-domain-settings
Feb 23, 2026
Merged

STA-19: proposal-first ENS trait + subdomain management#50
gertsio merged 5 commits intomainfrom
agent/STA-19-manage-domain-settings

Conversation

@gertsio
Copy link
Copy Markdown
Collaborator

@gertsio gertsio commented Feb 23, 2026

Summary

  • add proposal-first ENS management on /dashboard/ens for trait updates (avatar, description, url)
  • add subdomain list/create/revoke proposal UX with pending confirmation refresh
  • add Safe proposal API route and client signer flow
  • centralize Safe API config and return deterministic SAFE_API_KEY_MISSING errors
  • add graceful UI fallback when Safe proposal service is not configured

Technical Changes

  • new ENS dashboard components: traits card, subdomain manager, expiry defer card
  • new route boundaries: loading.tsx, error.tsx
  • extend startupChainAbi with subdomain methods
  • add server snapshot helpers for ENS traits + subdomains
  • add shared safe-api-config helper for explicit Safe API key wiring

Validation

  • ./node_modules/.bin/tsc --noEmit
  • yarn run test:unit
  • yarn run check (passes; existing warnings only, no errors)

Greptile Summary

This PR adds proposal-first ENS management to the /dashboard/ens route, allowing founders to propose ENS trait updates (avatar, description, url) and subdomain operations (create, revoke) through Safe multisig proposals with automatic confirmation tracking.

Key additions:

  • New /api/safe/propose route that accepts Safe transaction proposals from clients and submits them via Safe API Kit
  • Centralized safe-api-config helper that explicitly validates SAFE_API_KEY and returns deterministic SAFE_API_KEY_MISSING errors
  • ENS management modules with transaction builders for trait updates and subdomain management
  • Server-side snapshot helpers that fetch current ENS traits and subdomains with graceful fallbacks for unsupported methods
  • Interactive UI cards for traits and subdomains with pending state tracking and 15-second refresh polling
  • Proper route boundaries (loading.tsx, error.tsx) following Next.js 16 patterns
  • Comprehensive test coverage for API routes, config validation, and transaction encoding

Architecture highlights:

  • Proposal flow: User edits trait → Client signs via @safe-global/protocol-kit → POST to /api/safe/propose → Safe API Kit submits to queue → UI polls for confirmation
  • Graceful degradation when SAFE_API_KEY is missing (UI shows informational message, actions disabled)
  • Contract method availability detection for subdomain features (forwards-compatible with older deployments)
  • Extended startupChainAbi with subdomain methods (createSubdomain, revokeSubdomain, getSubdomain, getCompanySubdomains)

All changes align with documented system architecture and follow Next.js 16 + React 19 server component patterns.

Confidence Score: 5/5

  • Safe to merge with high confidence - well-tested feature with graceful degradation
  • All implementation files have comprehensive unit tests (safe-api-config.test.ts, ens-management.test.ts, route.test.ts), proper error handling with deterministic error codes, graceful UI fallbacks for missing API keys, contract method availability detection for backwards compatibility, and validation passes (tsc, yarn test:unit, yarn check). Code follows established patterns, uses proper TypeScript types, and includes route boundaries.
  • No files require special attention

Important Files Changed

Filename Overview
src/lib/blockchain/safe-api-config.ts Centralized Safe API key configuration with deterministic error handling
src/app/api/safe/propose/route.ts Server-side Safe proposal API with proper validation and error handling
src/lib/blockchain/safe-proposal-client.ts Client-side Safe proposal signer flow with proper error handling
src/lib/blockchain/ens-management.ts ENS trait and subdomain transaction builders with proper validation
src/lib/blockchain/ens-management-server.ts Server-side ENS trait and subdomain snapshot fetching with graceful fallbacks
src/app/(app)/dashboard/ens/page.tsx ENS dashboard with traits, subdomains, and expiry management cards
src/app/(app)/dashboard/ens/components/ens-traits-card.tsx Interactive ENS trait editor with Safe proposal flow and pending state tracking
src/app/(app)/dashboard/ens/components/subdomain-manager-card.tsx Subdomain create/revoke UI with Safe proposal flow and pending confirmations

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as ENS Traits Card
    participant Wallet as Privy Wallet
    participant Client as safe-proposal-client
    participant API as /api/safe/propose
    participant SafeKit as Safe API Kit
    participant Safe as Safe Contract
    participant ENS as ENS Resolver

    User->>UI: Edit trait & click "Propose update"
    UI->>Wallet: Connect wallet (if needed)
    Wallet-->>UI: Wallet ready
    UI->>Client: proposeSafeTransactionFromWallet()
    Client->>Wallet: Initialize Safe protocol-kit
    Wallet-->>Client: Safe instance
    Client->>Client: Create Safe transaction
    Client->>Client: Generate safeTxHash
    Client->>Wallet: Sign safeTxHash
    Wallet-->>Client: Signature
    Client->>API: POST /api/safe/propose
    Note over API: Validate payload<br/>Check SAFE_API_KEY
    API->>SafeKit: getSafeApiKitConfig()
    SafeKit-->>API: Config with API key
    API->>SafeKit: proposeTransaction()
    SafeKit->>Safe: Submit proposal to queue
    Safe-->>SafeKit: Proposal queued
    SafeKit-->>API: Success
    API-->>Client: { safeTxHash }
    Client-->>UI: Success with safeTxHash
    UI->>UI: Mark as pending
    UI->>UI: Start 15s refresh interval
    
    Note over User,ENS: Later: Founders approve & execute via Safe wallet
    
    Safe->>ENS: setText(node, key, value)
    ENS-->>Safe: Trait updated
    
    UI->>UI: Periodic refresh
    UI->>ENS: Read trait value
    ENS-->>UI: New value confirmed
    UI->>UI: Clear pending state
Loading

Last reviewed commit: 4263b9f

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
startupchain Ready Ready Preview, Comment Feb 23, 2026 2:59am

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

18 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@gertsio
Copy link
Copy Markdown
Collaborator Author

gertsio commented Feb 23, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9bb1618020

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +45 to +48
const apiKit = new SafeApiKit(getSafeApiKitConfig(parsed.chainId))

await apiKit.proposeTransaction({
safeAddress: parsed.safeAddress,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate Safe proposal endpoint with authenticated ownership

The new POST /api/safe/propose handler forwards requests to Safe Transaction Service without any server-side auth/authorization check that the caller is a logged-in founder for the submitted safeAddress. Because this route uses the server SAFE_API_KEY, an anonymous client can repeatedly submit valid signed payloads for arbitrary safes and burn API quota/rate limits, which can block legitimate proposal submissions from your dashboard. Add a session check and ownership validation before calling apiKit.proposeTransaction.

Useful? React with 👍 / 👎.

Comment on lines +127 to +129
const subdomains = await Promise.all(
names.map(async (name) => {
const details = await client.readContract({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Deduplicate subdomain labels before fetching records

This code fetches details for every entry returned by getCompanySubdomains as-is, but the contract flow allows duplicates after revoke/recreate (labels are appended on create and never removed on revoke). In that case, the same subdomain is rendered multiple times as active (and later causes duplicate React keys in the list UI), even though it is a single logical record. Deduplicate names before the map (for example via Set) so each label is loaded once.

Useful? React with 👍 / 👎.

@gertsio gertsio merged commit 6cb521e into main Feb 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant