Repository navigation
Conversation
…ssal The promo follows the rebrand announcement's rule: wallets that exist when this version first runs are owed it once; fresh installs and new or imported wallets are not. The status survives wallet wipes, and a wipe marks it seen so onboarding never ends on it. The Home banner's dismissal is stored separately and fails closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Ethena launch promo and its Home banner look the same in light and dark mode. An optional colorScheme prop lets Button and HeaderFade draw one scheme's colors whatever the device's is, so the promo can use the standard secondary and tertiary buttons and the main tabs' header fade in their dark form. Existing callers don't pass it and are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Meet Ethena on Stellar": the wave and coins, the intro, a scroll cue, then USDe and sUSDe, each with Swap and Receive, and Learn more about Ethena. It's a root-stack screen that fades in, black with light text in both themes, under a transparent bar with a close button. - Swap opens Sushi's XLM-to-token pair in a new Discover tab and returns to the Discover tab. It's hidden while Discover is off. - Receive opens the account's QR code over the promo. - Learn more opens Ethena's docs in the in-app browser. - A second tap within a second is ignored, so a double tap can't open two tabs or screens. - The promo marks itself seen and tracks the view on mount. The coins are pngquant-compressed PNGs at 1x, 2x and 3x; the wave and token logos are SVGs exported from the design. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The promo's one-second entrance plays once the screen has faded in: the wave slides in from the left and the coins rise as both fade in over 800ms, the title and subtitle fade in from 200ms and the scroll cue from 400ms. Under Reduce Motion every part starts in place. Tapping the scroll cue brings it to rest 60pt below the bar, with the tokens under it. The bar picks up the main tabs' header fade, in its dark form, once the page has scrolled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e splash At launch the promo's eligibility is settled next to the rebrand announcement's, and a launch that owes the rebrand announcement shows only that, leaving the promo for the next launch. The promo opens once Home is up and nothing covers it: after the splash has finished (which the splash overlay now reports), and never over the wallet setup screen or the rebrand announcement. A hard lock and unlock in the same launch doesn't bring it back, and signing out drops the pending answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Meet Ethena on Stellar" sits between Home's action row and the token tabs and opens the launch promo. It looks the same in both themes, with the design's blue glow on the right, and dark mode adds a thin outline (an overlay, so nothing moves) to set it off from the black page. The ✕ dismisses it for good; the banner stays hidden while that choice loads and if it can't be read, so a dismissed banner never flashes back. The card and the ✕ are sibling buttons so a screen reader reaches both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a 667pt iPhone SE the promo's first screen is about as tall as its content, so the flexible space above the scroll cue collapsed and the cue sat right under the subtitle. The space now never drops below the app's default 24pt; taller screens, where it's much larger, don't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The launch-time gate let the promo open in two cases where nobody would see it. While the force-update notice or the maintenance screen stands in for the navigator, the navigation was dropped and the launch lost its promo. Under the lock overlay, which keeps the authenticated tree mounted, the entrance played behind the overlay. The gate is now a small tested function that also waits for an unlocked wallet and for those screens to go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Android Emulator preview APK is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-d8d94d7e26c35dc1837e |
|
iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-2f6f02222ae1733c66d0 |
…n iOS Review feedback. Tapping "Scroll to discover tokens" now stops the cue 20pt below the top bar instead of 60pt. The promo's route also fades in and out over 150ms on iOS, as it does on Android, instead of iOS's 500ms default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Temporary, for a UX check of the automatic showing. Revert before merge. Outside E2E builds, every launch now owes the promo, so it opens after the splash, or right after unlocking when the launch starts locked. It also opens right after creating or importing a wallet. E2E builds keep the real rule, so CI's flows aren't affected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An Image sizes a bundled asset to the asset's own height unless its style sets one, so the glow stayed 78pt tall. The card is about that tall on iOS, but Android renders its text taller, which left a dark strip under the glow. The glow now takes the card's full height. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review feedback: tapping "Scroll to discover tokens" now stops the cue 5pt below the top bar, down from 20pt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read On the iPhone 18 Pro simulator the promo took about 1.5s to appear after a banner tap. Drawing the wave's ~200 gradient strokes as SVG held the UI thread for over a second on 3x screens. The wave is now a bitmap of the same art (@1x, @2x and @3x, cropped to its box), and the promo appears about 0.25s after the tap. After an unlock, the JS thread stays busy for a few seconds while Home loads. The entrance waited for it to deliver the transition's end, so the art sat on a black page. The entrance is now handed to the UI thread as the promo mounts and starts as its 150ms fade-in ends. The fade's length is shared with the route's options. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Now that nothing holds the promo's opening back, iOS fades it in and out over its default 500ms again, which looks better there than Android's 150ms. Android keeps its 150ms fade. The entrance still starts as the fade-in ends, on each platform. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit 911ab2b. The UX check is done, so the promo is back to opening once, for wallets that existed before the update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The eligibility decision is not terminal when the initial status or account check fails, allowing a later retry to incorrectly show the promo for an existing wallet.
1 open issue
What changed in this PR
Adds the Ethena launch experience to the mobile wallet, including a dismissible Home banner and a full-screen USDe/sUSDe promotion with launch eligibility, token actions, analytics, localization, and supporting assets.
Changes:
- Adds persistent eligibility, banner dismissal, rebrand sequencing, wallet-wipe handling, splash synchronization, and navigation integration.
- Adds the Ethena promotional screen with Sushi Discover, Receive QR, Learn more, animations, and dark-pinned styling.
- Adds English and Portuguese translations, artwork, analytics events, and comprehensive unit and component tests.
| File | Description |
|---|---|
| src/services/storage/helpers.ts | Preserves Ethena launch eligibility while clearing non-sensitive wallet data. |
| src/services/analytics/discover.ts | Adds the Ethena promo source for Discover analytics. |
| src/navigators/RootNavigator.tsx | Integrates Ethena eligibility, automatic display gating, splash coordination, and routing. |
| src/i18n/locales/pt/translations.json | Adds Portuguese Ethena promo and banner copy. |
| src/i18n/locales/en/translations.json | Adds English Ethena promo and banner copy. |
| src/hooks/useEthenaLaunchPromoAutoShow.ts | Implements promo eligibility gating and one-display-per-launch behavior. |
| src/hooks/useEthenaLaunchBanner.ts | Loads and persists Home banner visibility and dismissal state. |
| src/helpers/rebrandAnnouncement.ts | Exports existing-account detection for shared launch eligibility logic. |
| src/helpers/ethenaLaunchPromo.ts | Implements Ethena promo status, eligibility, and banner dismissal persistence. |
| src/ducks/auth.ts | Marks the promo as seen during wallet reset. |
| src/config/routes.ts | Adds the Ethena route, promo source type, and navigation parameters. |
| src/config/constants.ts | Defines Ethena storage keys and lifecycle documentation. |
| src/config/analyticsConfig.ts | Adds Ethena promo and banner analytics events. |
| src/components/SplashAnimation/SplashOverlay.tsx | Exposes a callback for one-time splash completion handling. |
| src/components/sds/Button/index.tsx | Supports explicitly pinned button color schemes. |
| src/components/screens/HomeScreen/HomeScreen.tsx | Places the Ethena banner between the Home action row and token tabs. |
| src/components/screens/HomeScreen/EthenaLaunchBanner.tsx | Implements the dismissible Ethena Home banner and its artwork. |
| src/components/screens/EthenaLaunchScreen/useEthenaLaunchEntrance.ts | Provides the promo entrance animation and reduced-motion behavior. |
| src/components/screens/EthenaLaunchScreen/scrollCue.ts | Calculates the scroll target for the token discovery cue. |
| src/components/screens/EthenaLaunchScreen/index.ts | Exports the Ethena launch screen. |
| src/components/screens/EthenaLaunchScreen/EthenaTokenSection.tsx | Renders Ethena token details and Swap and Receive actions. |
| src/components/screens/EthenaLaunchScreen/EthenaLaunchScreen.tsx | Implements the full-screen promo, navigation actions, and analytics. |
| src/components/screens/EthenaLaunchScreen/EthenaLaunchHero.tsx | Renders the promo hero artwork, copy, and scroll cue. |
| src/components/screens/EthenaLaunchScreen/ethenaLaunchConfig.ts | Defines Ethena tokens, URLs, artwork, colors, and animation timing. |
| src/components/layout/HeaderFade.tsx | Supports header fades pinned to a specific color scheme. |
| src/assets/logos/usde.svg | Adds the USDe logo asset. |
| src/assets/logos/susde.svg | Adds the sUSDe logo asset. |
| src/assets/images/index.ts | Registers the Ethena promotional image assets. |
| tests/services/analytics/discover.test.ts | Tests the Ethena promo Discover analytics source. |
| tests/hooks/useEthenaLaunchPromoAutoShow.test.tsx | Tests promo eligibility gating and one-time automatic display. |
| tests/hooks/useEthenaLaunchBanner.test.tsx | Tests banner loading, dismissal, persistence, and analytics. |
| tests/helpers/ethenaLaunchPromo.test.ts | Tests promo eligibility, persistence, storage failures, dismissal, and wallet lifecycle behavior. |
| tests/ducks/auth.test.ts | Verifies that wallet wipes mark the promo as seen. |
| tests/config/theme.test.ts | Tests registration of the pinned Ethena theme and logo assets. |
| tests/components/SplashAnimation/SplashOverlay.test.tsx | Tests the splash completion callback. |
| tests/components/sds/Button.test.tsx | Tests pinned button color schemes. |
| tests/components/screens/HomeScreen/EthenaLaunchBanner.test.tsx | Tests banner rendering, actions, styling, themes, and glow sizing. |
| tests/components/screens/EthenaLaunchScreen/useEthenaLaunchEntrance.test.tsx | Tests promo animation timing and reduced-motion behavior. |
| tests/components/screens/EthenaLaunchScreen/scrollCue.test.ts | Tests scroll cue target calculations. |
| tests/components/screens/EthenaLaunchScreen/EthenaLaunchScreen.test.tsx | Tests promo actions, navigation, analytics, styling, and scrolling. |
| tests/components/screens/EthenaLaunchScreen/ethenaLaunchConfig.test.ts | Tests platform-specific promo transition timing. |
| tests/components/layout/HeaderFade.test.tsx | Tests pinned dark header fades across device themes. |
Review effort: Lite

TL;DR
Adds the Ethena launch: a dismissible "Meet Ethena on Stellar" banner on Home and a full-screen promo for USDe and sUSDe. The promo opens from the banner, and once on its own for existing wallets after they update. From it, Swap opens Sushi's swap page for the token in a new Discover tab, Receive shows the account's QR code, and "Learn more about Ethena" opens Ethena's docs. The one-time showing follows the rebrand announcement's rule, as the extension does in stellar/freighter#3095: fresh installs and new or imported wallets only get the banner, and a launch that shows the rebrand announcement leaves the promo for the next launch.
Follow-ups
token0andtoken1params to its Stellar swap page soon; until that ships, Swap opens on Sushi's default pair with USDT0 as destination.Video samples (light theme variation shown at the end)
ethena-launch-ios.mov
ethena-launch-android.mov
Implementation details (for agents)
What changed
src/helpers/ethenaLaunchPromo.ts):pendingorseen. Each install decides it once at launch, before a wallet can be created:pendingwhen accounts are already stored (an update),seenotherwise (a fresh install). This is the rebrand announcement's rule, andhasExistingAccountsis now exported fromhelpers/rebrandAnnouncement.seen, so the promo never shows by mistake.clearNonSensitiveData, which also runs on sign-up and import, and the wipe path marks itseennext to the rebrand's status.RootNavigator(eligibility, auto-show):pendinguntil the next launch.useEthenaLaunchPromoAutoShowopens it once per launch whencanShowEthenaLaunchPromoholds: the wallet is unlocked, the lock overlay is down, and the splash has finished (SplashOverlaygains anonFinishedprop). None of these may be up: the wallet setup screen, the rebrand announcement, the force-update notice or the maintenance screen.ETHENA_LAUNCH_SCREENin the authenticated RootStack, with{ source: "launch" | "banner" }and each platform's own fade:ETHENA_LAUNCH_FADE_MSis iOS's default 500ms (passed asanimationDuration, so the entrance stays in step) and Android's fixed 150ms. There's no deep link.src/components/screens/EthenaLaunchScreen/):seenand tracksethena_launch.promo_viewedwith itssource.HeaderFadeshows once the page has scrolled. Android's back button closes it too.useEthenaLaunchEntrance) uses the design's motion timings and curves. It's handed to the UI thread as the promo mounts and starts as the fade-in ends, so a busy JS thread (Home loading after an unlock) can't hold it back. Reduce Motion skips it.scrollCue.ts).discover_enabledis off.https://docs.ethena.fi/in the in-app browser. A 1-second guard ignores double taps.ethenaLaunchConfig.ts). For that,ButtonandHeaderFadegain an optionalcolorSchemeprop, and callers that don't pass it are unchanged.HomeScreen/EthenaLaunchBanner.tsx,hooks/useEthenaLaunchBanner.ts):HomeScreen).ethena_launch.banner_dismissed. Resetting the wallet clears the dismissal along with the rest of the non-sensitive data.ethena_launch.promo_viewed,ethena_launch.swap_tappedandethena_launch.receive_tapped(both withtoken),ethena_launch.learn_more_tappedandethena_launch.banner_dismissed. Discover tabs opened from the promo use the newethena_launch_promosource.Product decisions
https://www.sushi.com/stellar/swap?token0=CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA&token1=CCNOV6XVI2O5BSTA3232GIRUQULDEBV7IWGKM6QKI6II5OIIQTU24JJNhttps://www.sushi.com/stellar/swap?token0=CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA&token1=CACSKWC753QVGC4J6YPDBIR2BEHFVWIAE7CBSS3HOVYMN623OAIQYE2MParity with stellar/freighter#3095: the same one-time rule and the same Sushi URLs, opened in a new Discover tab instead of a browser tab. Mobile has no "Transfer to Stellar" (Stargate) action, as in the mobile design, and its cue also scrolls when tapped.
Verification
yarn testpasses: 355 suites (1 skipped) and 5,242 tests.tscis clean, and ESLint and Prettier are clean on every changed file.yarn checkas a whole still fails on 106 ESLint errors in 18 files this PR doesn't change, as it does onmain.seen;SplashOverlay'sonFinished, and the newButtonandHeaderFadeprop.Notes
RootNavigatorhas no test harness. Both were checked on devices.Before merge
911ab2b0, reverted ina42d5794).🤖 Generated with Claude Code