Skip to content

feat: replace the USDT0 promo with the Ethena launch on Home - #3095

Open
CassioMG wants to merge 12 commits into
masterfrom
feature/ethena-stargate-launch
Open

CassioMG wants to merge 12 commits into
masterfrom
feature/ethena-stargate-launch

Conversation

@CassioMG

@CassioMG CassioMG commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

Replaces the USDT0 launch on Home with the Ethena launch: USDe and sUSDe on Stellar, with Stargate for transfers. A "Meet Ethena on Stellar" banner on Home opens a full-screen promo that plays Figma's intro animation and lists both tokens, each with Swap (Sushi, from XLM to the token), Receive (the wallet's address screen) and Transfer to Stellar (Stargate, from Ethereum with the token preselected). "Learn more about Ethena" opens Ethena's docs.

Wallets that already exist when this version installs also see the promo once, on their next unlock to Home. This reuses the trigger of the Varial rebrand announcement: new and imported wallets skip it, so it doesn't follow onboarding (Home's banner still opens it). The banner and the promo look the same in light and dark themes, except that dark mode outlines the banner so it stands out on the black page. The USDT0 banner and sheet are removed.

Designs: Figma. The Discover screen in that section isn't part of this PR.

Follow-ups

  • Sushi is adding token0 and token1 params to its Stellar swap page soon; until that ships, Swap opens on Sushi's default pair with USDT0 as destination.

Video sample (light theme variation shown at the end)

ethena-launch.mov
Implementation details (for agents)

What changed

  • One-time promo trigger, a copy of the rebrand announcement's:
    • markEthenaLaunchPromoEligibility (dataStorage.ts#L408-L428) runs on runtime.onInstalled (index.ts#L215). An install that finds wallets marks ethenaLaunchPromoStatus pending; one that finds none marks it seen. A status that's already set is kept, so later updates don't re-arm it.
    • Creating or recovering a wallet marks it seen (createAccount.ts#L81-L90, recoverAccount.ts#L88-L97), so onboarding is never followed by the promo.
    • CLAIM_ETHENA_LAUNCH_PROMO (claimEthenaLaunchPromo.ts) turns pending into seen and answers whether to show it. Claims run one at a time, so two surfaces can't both win.
    • UnlockAccount (index.tsx#L78-L115) claims it only when the unlock is headed Home, after the rebrand claim, and fails closed. It passes the destination in route state, and closing the promo returns there.
  • Promo view EthenaLaunch (index.tsx, styles.scss), at /ethena-launch behind VerifiedAccountRoute, screen name ethena_launch:
    • Pinned to the dark theme (sds-theme-dark on its root), since its hero art is drawn for a black page.
    • Figma's 1s entrance as CSS keyframes (styles.scss#L3-L11, #L360-L390): the wave and coins slide in over 800ms, the copy fades in from 200ms and the scroll cue from 400ms. No motion under prefers-reduced-motion.
    • The close button sits on a sticky bar as tall as Home's pinned header, with the same fade (styles.scss#L45-L73; the fade is now a shared mixin, utils.scss#L32-L41). The bar is clear at the top, where the wave runs up behind the X, and fills in once the page scrolls, so content fades out under it.
    • Tapping "Scroll to discover tokens" scrolls the page until the cue sits 20px below the bar, the tokens under it, smoothly or straight there under reduced motion (index.tsx#L40-L43, #L166-L181).
    • In fullscreen mode it sits in the same frame as Home (View's inset: centred, up to 848px, with its vertical borders), and its content fills the frame, so its buttons span it like other fullscreen screens' (index.tsx#L208-L215, styles.scss#L34-L43). In the popup and sidebar the frame is the whole window. Where a wide frame would show the wave's strokes cut off, they fade out.
    • Swap and Receive are SDS Buttons (lg, secondary and tertiary). Transfer is hand-rolled because no SDS variant is transparent, at the same 40px height with the same 14px arrow (styles.scss#L287-L320).
    • Receive opens the address screen, whose X goes back in history. The promo first notes its scroll position in its own history entry, so it comes back as it was left, without replaying the intro (index.tsx#L190-L201, #L152-L160).
    • Esc closes the promo (to where its X goes) and the address screen (back), not the whole popup. A small useEscapeToClose hook (useEscapeToClose.ts) marks the key handled, which keeps Chrome from closing the popup (index.tsx#L187-L188, ViewPublicKey/index.tsx#L34-L35).
    • Links (index.tsx#L19-L38): Sushi's Stellar swap from XLM to the token, passed as token0 and token1 contract addresses; Stargate from Ethereum to Stellar with the token preselected on both sides; Ethena's docs. The USDe and sUSDe addresses are the ones LayerZero's transfer token list publishes.
  • Home banner EthenaLaunchBanner (index.tsx, styles.scss), in AccountHeader where the USDT0 banner was:
    • The USDT0 dismissal plumbing is renamed (GET_ETHENA_LAUNCH_BANNER_DISMISSED, DISMISS_ETHENA_LAUNCH_BANNER, key ethenaLaunchBannerDismissed). The banner stays hidden while that loads, and if the lookup fails.
    • It's Figma's banner (4414:42195) in both themes, with fixed colors allowlisted in the palette guard. Dark mode adds the 1px gray-04 outline from Figma's dark Home (4414:59535) (styles.scss#L109-L115). It's an inset outline, so it takes no space and nothing on the card moves.
    • color-scheme: dark keeps the browser's focus ring the same in both themes. The card's main button draws it inside, since the card clips anything outside it and the outline covers its edge.
  • Removed: the USDT0 banner and sheet, their assets and 11 translation keys.
  • i18n: 11 new strings in en and pt. The pt strings are machine-authored; native review welcome.
  • Assets from Figma: the wave SVG, two coin PNGs (downscaled to 2x, about 240 KB together), the banner glow, and the USDe and sUSDe logos.
  • Differences from Figma: the title uses Inter, since Funnel Sans isn't bundled (it still fits on one line). Swap, Receive and Transfer are all SDS's 40px button height rather than Figma's 36px and 32px, so the page is 32px taller. The banner keeps Home's 16px gap to the tabs instead of 8px.

Verification

  • yarn test:ci: 2,707 tests pass, 0 failures. New tests cover the claim, the eligibility on install, create and recover marking it seen, the dismissal handlers, the unlock routing (promo on the way Home, fail closed, a dApp request goes first, the rebrand announcement goes first), the promo view (links, Receive, close, the bar, the scroll cue, coming back from Receive, Esc), the Esc hook and the banner.
  • yarn build:extension passes. ESLint and Prettier are clean on the changed files.
  • E2E: theme.test.ts 5/5 and the new ethenaLaunch.test.ts 10/10. Two new theme tests (theme.test.ts#L89-L223) diff every computed style of the banner and of the promo across a live light-to-dark switch; the only change they accept is the banner's dark-mode outline. They also check the banner against Figma's values. ethenaLaunch.test.ts checks that a wallet set up on this install skips the promo and can open it from the banner, that a wallet that predates the launch sees it once, that the promo sits in Home's frame in a wide window and fills it, that the token actions are all the same size, that the scroll cue stops 20px below the bar, that the promo comes back from Receive as it was left, that Esc closes the address screen and then the promo, that the close button stays put on the bar as the page scrolls, and that keyboard focus on the banner's main button draws a full ring in both themes.
  • E2E full suite (yarn test:e2e): passes in CI on cf07839 (the test check).
  • On a real build: compared against Figma at 360×600 (positions match; the page runs 32px longer because of the buttons), including the dark-mode outline, at 450px and 700px wide, and in fullscreen mode at 1280px. The bar and the frame leave the promo's first screen in the popup pixel-identical.

🤖 Generated with Claude Code

CassioMG and others added 3 commits November 13, 2026 10:52
Swaps the USDT0 launch banner and sheet for the Ethena (USDe and sUSDe)
launch, following the Varial Figma "Promo banner" section.

- Home banner "Meet Ethena on Stellar": dismissible, on the same
  background-owned storage plumbing the USDT0 banner used (renamed
  SERVICE_TYPES, handlers, storage key and internal API).
- Full-screen promo at /ethena-launch with Figma's 1s entrance animation
  (off under prefers-reduced-motion). Per token: Swap opens Sushi's Stellar
  swap, Receive the address screen, and Transfer to Stellar opens Stargate
  from Ethereum with the token preselected on both sides.
- Shown once after the update, on the Varial announcement's trigger:
  wallets that exist when the build installs are marked pending, fresh
  installs and new or imported wallets seen, and UnlockAccount claims it
  once on the way Home. A dApp request isn't held up for it, and when the
  rebrand announcement is also owed that plays first and the promo waits
  for a later unlock.
- Tapping the banner opens the same promo again.

Removes the USDT0 sheet, its assets and its strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The banner is one design in both themes, Figma's (4414:42195): no
dark-mode outline, and the X keeps Figma's gray instead of following
the theme's gray-10. Its copy and the browser-drawn focus ring are
pinned too, so nothing on the card flips with the theme.

The theme e2e tests now diff every computed style of the banner and of
the full-screen promo across a live theme change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

PR Preview build is ready: https://github.com/stellar/freighter/releases/tag/untagged-22ee77e0d8fa1044dc0e
Backend: sandbox (CassioMG). SDF collaborators only — install instructions in the release description.

CassioMG and others added 9 commits November 13, 2026 15:39
Figma's dark Home (4414:59535) outlines the banner in gray-04, so the
near-black card stands out on the black page. It's an inset outline:
it takes no space, so nothing on the card moves.

The outline covers the card's edge, where the only visible part of the
keyboard focus ring on the card's main button was drawn (the card clips
the rest). The ring now goes inside the button, and a new e2e test
checks it shows all the way round in both themes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The one-time promo now shows once to every wallet, new or existing,
  on an unlock to Home (after the rebrand announcement if that's owed).
  It's a plain "has seen it" flag: no install-time marking, and creating
  or importing a wallet no longer marks it seen. Onboarding e2e flows
  that unlock a wallet for the first time close the promo first, and a
  new e2e test covers once-and-only-once.
- Swap opens Sushi's Stellar swap from XLM to the token, by contract
  address (token0/token1), which Sushi is adding.
- The close button sits on a sticky bar that fills in once the page
  scrolls, with Home's header fade, now a shared mixin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Back to the Varial announcement's rule: wallets that exist when this
version installs get the promo once, on their next unlock to Home.
Fresh installs, and wallets created or imported after, skip it so it
doesn't follow onboarding; Home's banner still opens it.

This restores the trigger from before 85ad74b and keeps that commit's
Sushi links and sticky bar. Two e2e tests cover the rule: a wallet set
up on this install skips it and can open it from the banner, and a
wallet that predates the launch sees it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Transfer to Stellar is now the height of SDS's lg Button, like Swap and
  Receive (40px, 14/22 text), with a 14px arrow like theirs.
- "Scroll to discover tokens" is a button: it scrolls the page until the
  cue sits 60px below the close button's bar, the tokens under it
  (smoothly, or straight there under reduced motion).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Receive opens the address screen, whose X goes back in history, which
remounted the promo at the top and replayed its intro. The promo now
notes its scroll position in its own history entry before it goes, and
coming back restores it before the first paint, with the close button's
bar already in place and no intro.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In fullscreen mode the promo took the whole tab. It now sits in the
same frame as Home (View's inset: centred, up to 848px, with its
vertical borders from 850px), and its content fills the frame instead
of a fixed 360px column, so its buttons span it like other fullscreen
screens' do. The popup is unchanged, pixel for pixel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chrome closes an extension popup on an Esc the page leaves unhandled.
A small useEscapeToClose hook marks it handled and closes the screen
instead: the promo goes where its X goes, and the address screen goes
back, like its X. An Esc something else already handled is left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CassioMG
CassioMG marked this pull request as ready for review November 14, 2026 02:41
@CassioMG
CassioMG requested a lite review from Copilot November 14, 2026 02:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Active issues remain because concurrent unlock surfaces can both display takeover screens, and the new claim failure path silently drops messaging errors instead of reporting them.

2 open issues
What changed in this PR

Replaces the USDT0 Home promotion with an Ethena launch experience for USDe and sUSDe on Stellar. The PR adds one-time eligibility handling, a full-screen token guide, a new Home banner, updated messaging and localization, and removes the previous USDT0 campaign.

Changes:

  • Adds the Ethena promo route, animations, token actions, receive flow, external links, accessibility behavior, and metrics.
  • Adds persistent eligibility and claim handling, unlock routing, background messaging, and onboarding exclusions.
  • Replaces USDT0 components, translations, styles, and assets with Ethena equivalents and adds comprehensive unit and E2E coverage.
File Description
extension/​src/​popup/​views/​ViewPublicKey/​index.tsx Adds Escape-to-back behavior for the address screen.
extension/​src/​popup/​views/​UnlockAccount/​index.tsx Claims and routes eligible unlocks to the Ethena promo.
extension/​src/​popup/​views/​UnlockAccount/​__tests__/​UnlockAccount.test.tsx Tests Ethena promo routing, priority, and failure handling.
extension/​src/​popup/​views/​EthenaLaunch/​styles.scss Defines the full-screen promo layout, animation, scrolling, and responsive styling.
extension/​src/​popup/​views/​EthenaLaunch/​index.tsx Implements the Ethena full-screen launch experience and token actions.
extension/​src/​popup/​views/​__tests__/​EthenaLaunch.test.tsx Tests promo content, actions, navigation, scrolling, and Escape handling.
extension/​src/​popup/​styles/​utils.scss Adds the shared pinned-header fade mixin.
extension/​src/​popup/​styles/​__tests__/​varialPalette.test.ts Updates the fixed-color allowlist for Ethena campaign styling.
extension/​src/​popup/​Router.tsx Registers the verified Ethena promo route.
extension/​src/​popup/​metrics/​views.ts Adds Ethena screen metrics.
extension/​src/​popup/​locales/​pt/​translation.json Adds Portuguese Ethena translations and removes USDT0 strings.
extension/​src/​popup/​locales/​en/​translation.json Adds English Ethena translations and removes USDT0 strings.
extension/​src/​popup/​helpers/​hooks/​useEscapeToClose.ts Adds reusable Escape-to-close behavior.
extension/​src/​popup/​helpers/​hooks/​__tests__/​useEscapeToClose.test.ts Tests Escape handling and cleanup.
extension/​src/​popup/​constants/​routes.ts Adds the Ethena route constant.
extension/​src/​popup/​components/​account/​Usdt0LaunchBanner/​Usdt0LaunchSheet/​styles.scss Removes the obsolete USDT0 sheet styles.
extension/​src/​popup/​components/​account/​Usdt0LaunchBanner/​Usdt0LaunchSheet/​index.tsx Removes the obsolete USDT0 sheet component.
extension/​src/​popup/​components/​account/​Usdt0LaunchBanner/​styles.scss Removes the obsolete USDT0 banner styles.
extension/​src/​popup/​components/​account/​Usdt0LaunchBanner/​index.tsx Removes the obsolete USDT0 banner component.
extension/​src/​popup/​components/​account/​EthenaLaunchBanner/​styles.scss Styles the new Ethena Home banner across themes.
extension/​src/​popup/​components/​account/​EthenaLaunchBanner/​index.tsx Implements Ethena banner dismissal and promo navigation.
extension/​src/​popup/​components/​account/​EthenaLaunchBanner/​__tests__/​EthenaLaunchBanner.test.tsx Tests banner loading, dismissal, errors, and navigation.
extension/​src/​popup/​components/​account/​AccountHeader/​index.tsx Replaces the USDT0 banner with the Ethena banner.
extension/​src/​popup/​basics/​layout/​View/​styles.scss Uses the shared header fade mixin.
extension/​src/​popup/​assets/​usdt0-lockup.svg Removes the obsolete USDT0 lockup asset.
extension/​src/​popup/​assets/​usdt0-arcs.svg Removes the obsolete USDT0 artwork.
extension/​src/​popup/​assets/​logo-usde.svg Adds the USDe token logo.
extension/​src/​popup/​assets/​logo-susde.svg Adds the sUSDe token logo.
extension/​src/​popup/​assets/​ethena-launch-banner-glow.svg Adds the Ethena banner glow artwork.
extension/​src/​constants/​localStorageTypes.ts Adds Ethena banner and promo storage keys.
extension/​src/​constants/​ethenaLaunchPromo.ts Defines Ethena promo eligibility states.
extension/​src/​background/​messageListener/​popupMessageListener.ts Registers Ethena banner and promo message handlers.
extension/​src/​background/​messageListener/​handlers/​recoverAccount.ts Prevents recovered wallets from inheriting the promo.
extension/​src/​background/​messageListener/​handlers/​getEthenaLaunchBannerDismissed.ts Reads Ethena banner dismissal state.
extension/​src/​background/​messageListener/​handlers/​dismissEthenaLaunchBanner.ts Persists Ethena banner dismissal state.
extension/​src/​background/​messageListener/​handlers/​createAccount.ts Prevents newly created wallets from inheriting the promo.
extension/​src/​background/​messageListener/​handlers/​claimEthenaLaunchPromo.ts Atomically claims the one-time Ethena promo.
extension/​src/​background/​messageListener/​__tests__/​resetWallet.test.ts Updates reset-wallet coverage for Ethena storage state.
extension/​src/​background/​messageListener/​__tests__/​recoverAccount.test.ts Tests recovered-wallet promo state.
extension/​src/​background/​messageListener/​__tests__/​ethenaLaunchBanner.test.ts Tests Ethena banner storage handlers.
extension/​src/​background/​messageListener/​__tests__/​createAccount.test.ts Tests new-wallet promo state.
extension/​src/​background/​messageListener/​__tests__/​claimEthenaLaunchPromo.test.ts Tests one-time and concurrent promo claims.
extension/​src/​background/​index.ts Registers install-time Ethena promo eligibility initialization.
extension/​src/​background/​helpers/​dataStorage.ts Determines promo eligibility during installation and updates.
extension/​src/​background/​helpers/​__tests__/​migrations.test.ts Tests promo eligibility initialization and persistence.
extension/​e2e-tests/​theme.test.ts Verifies Ethena theme invariance and banner styling.
extension/​e2e-tests/​ethenaLaunch.test.ts Covers end-to-end promo lifecycle, layout, actions, scrolling, and keyboard behavior.
@shared/​constants/​services.ts Adds Ethena internal message types.
@shared/​api/​types/​types.ts Adds shared typing for the Ethena promo response.
@shared/​api/​types/​message-request.ts Defines Ethena message request types.
@shared/​api/​internal.ts Exposes Ethena dismissal and promo-claim APIs.

Review effort: Lite

@CassioMG

Copy link
Copy Markdown
Contributor Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

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.

2 participants