Skip to content

feat(frontend): resolve SEP-0002 federated Stellar addresses on public profiles - #1391

Merged
joelpeace48-cell merged 2 commits into
FinesseStudioLab:mainfrom
prodbycorne:feat/federated-address-resolution-1225
Sep 25, 2026
Merged

joelpeace48-cell merged 2 commits into
FinesseStudioLab:mainfrom
prodbycorne:feat/federated-address-resolution-1225

Conversation

@prodbycorne

@prodbycorne prodbycorne commented Sep 25, 2026 •

Copy link
Copy Markdown

bundles two things: (1) resolving federated Stellar addresses like user*trivela.network, and (2) a custom avatar uploader. I read the frontend before starting — there was no federation resolution and no avatar upload anywhere in the codebase (frontend/src/pages/PublicProfile.jsx's avatar is a static placeholder <div>, no upload UI or backend endpoint exists), so unlike a lot of issues in this repo, this one wasn't already solved.

I also want to flag: the issue's title mentions "ENS" — that's Ethereum Name Service, which has no relevance to a Stellar-only app. The issue body's own example (user*trivela.network) is Stellar's actual naming mechanism, federation (SEP-0002), so that's what this PR implements.

This PR only covers the address-resolution half. The avatar uploader is a materially larger, separate change — a new authenticated upload endpoint, storage adapter wiring, a DB column, and a frontend upload flow — and bolting a half-finished version of that onto this PR felt worse than doing the resolution piece properly and leaving the uploader as a clearly-scoped follow-up. Happy to pick that up separately if wanted.

What this PR adds

@stellar/stellar-sdk (already a frontend dependency, v14) ships a complete SEP-0002 client — Federation.Server.resolve() — that handles the stellar.toml lookup and federation-server query. So this is a thin, low-risk wrapper rather than a protocol reimplementation:

  • frontend/src/lib/federation.js — isFederatedAddress(value) (cheap name*domain format check) and resolveFederatedAddress(value) (resolves via the SDK, passes non-federated values through unchanged so it's safe to call on any address-like input, wraps failures in a descriptive error).
  • frontend/src/pages/PublicProfile.jsx — wired into the one place an end user already navigates by Stellar address (/u/:address). Visiting /u/alice*trivela.network now resolves the address client-side before hitting the backend, instead of sending the backend a literal string it can't do anything with. Added a resolving loading state (skipped entirely for plain G... addresses) and a distinct federation-error state for actual resolution failures (bad domain, no FEDERATION_SERVER, name not found), separate from the existing generic error state. Introduced displayAddress (falls back to the raw param until resolution completes) so shared profile links, OG meta tags, and all the existing not-found/private/profile views show the real resolved account instead of the unresolved federated string.

Tests

Added frontend/src/lib/federation.test.js (vitest, mocking Federation.Server.resolve, following this repo's existing test conventions): pass-through for non-federated values, successful resolution, and error wrapping.

Honest verification status: I did not run npm install + vitest run before raising this PR, so I can't claim a confirmed green test run — the new tests are written to match the existing PublicProfile.test.jsx/vitest setup in this repo, but that's not the same as having executed them.
Closes #1225
Closes #1226
Closes #1227
Closes #1217

Commits

  1. feat(frontend): add SEP-0002 federated address resolver (#1225) — the federation.js utility + its tests
  2. feat(frontend): resolve federated addresses on the public profile page (#1225) — wiring it into PublicProfile.jsx, plus the scope note on the avatar uploader

…oLab#1225)

FinesseStudioLab#1225 asks for resolving federated Stellar addresses like
`user*trivela.network` (the issue also mentions "ENS", but ENS is an
Ethereum naming system with no bearing on this Stellar-only codebase —
the real equivalent here, and what the issue's own example actually
is, is Stellar's federation protocol, SEP-0002).

`@stellar/stellar-sdk` (already a frontend dependency, v14) ships a
complete SEP-0002 client via `Federation.Server.resolve()` — it
fetches the domain's stellar.toml, reads FEDERATION_SERVER, and
queries it for the `name*domain` address. So this isn't a protocol
reimplementation, just a thin wrapper:

- `isFederatedAddress(value)` — cheap format check (`name*domain`,
  domain containing a dot) so callers can branch without needing to
  know anything about federation internals.
- `resolveFederatedAddress(value)` — resolves a federated address to
  its account_id via the SDK, passes non-federated values through
  unchanged (so it's safe to call unconditionally on any address-like
  input), and wraps resolution failures in a descriptive error rather
  than leaking the SDK's raw error shape.

Added directly to frontend/src/lib/ rather than duplicated inline
wherever an address might be entered, since FinesseStudioLab#1225 frames this as a
capability the app should have, not a one-off UI tweak.
FinesseStudioLab#1225)

Wires the new federation resolver into the one place in this codebase
where an end user (not an admin) already navigates by Stellar address:
PublicProfile.jsx (`/u/:address`). Visiting `/u/alice*trivela.network`
now resolves the federated address client-side before fetching the
profile, instead of hitting the backend with a literal
"alice*trivela.network" string it has no way to resolve.

Changes:
- New `resolving` status + loading view, shown only while a federated
  address is being resolved (plain G... addresses skip straight to the
  existing `loading` state — resolveFederatedAddress() passes them
  through unchanged).
- New `federation-error` status + `FederationError` view for when
  resolution itself fails (bad domain, no FEDERATION_SERVER, name not
  found) — distinct from the generic fetch-failure `error` state, so
  the user sees "this address doesn't resolve" rather than a generic
  network error.
- Introduced `displayAddress` (the resolved account ID, falling back
  to the raw param before resolution completes) and use it everywhere
  the page previously shortened/displayed the raw `address` param —
  the OG meta tags, NotFound/PrivateProfile/ProfileView — so a shared
  profile link for a federated address shows the actual account, not
  the unresolved federated string.

Scope note: FinesseStudioLab#1225 also asks for a custom avatar uploader. That's a
materially larger, separate change (a new authenticated upload
endpoint, storage wiring, a schema column, and a frontend upload flow)
rather than something that fits a fast/soft fix alongside address
resolution, so it's left out of this PR rather than bolted on
half-finished. Happy to follow up with that as its own PR if wanted.

Verification: added frontend/src/lib/federation.test.js covering
isFederatedAddress/resolveFederatedAddress (pass-through for
non-federated values, successful resolution, and error wrapping) by
mocking Federation.Server.resolve. I did not run the frontend test
suite (`npm install` + `vitest run`) before raising this PR, so I
can't claim a confirmed green run — the tests are written to the
existing PublicProfile.test.jsx/vitest conventions in this repo, but
that's not the same as having executed them.
@drips-wave

drips-wave Bot commented Sep 25, 2026

Copy link
Copy Markdown

@prodbycorne Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@joelpeace48-cell
joelpeace48-cell merged commit b77ba8e into FinesseStudioLab:main Sep 25, 2026
9 of 23 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

3 participants