diff --git a/.changeset/nasty-deers-clap.md b/.changeset/nasty-deers-clap.md
new file mode 100644
index 000000000000..c8506e809bf7
--- /dev/null
+++ b/.changeset/nasty-deers-clap.md
@@ -0,0 +1,7 @@
+---
+"@ledgerhq/types-live": patch
+"ledger-live-desktop": patch
+"@ledgerhq/live-common": patch
+---
+
+[swap] setup demo3 flag and routes
diff --git a/apps/ledger-live-desktop/src/renderer/analytics/segment.ts b/apps/ledger-live-desktop/src/renderer/analytics/segment.ts
index 0fa152742462..3264951f619f 100644
--- a/apps/ledger-live-desktop/src/renderer/analytics/segment.ts
+++ b/apps/ledger-live-desktop/src/renderer/analytics/segment.ts
@@ -85,6 +85,7 @@ const getPtxAttributes = () => {
const ptxSwapLiveAppDemoZero = analyticsFeatureFlagMethod("ptxSwapLiveAppDemoZero")?.enabled;
const ptxSwapLiveAppDemoOne = analyticsFeatureFlagMethod("ptxSwapLiveAppDemoOne")?.enabled;
+ const ptxSwapLiveAppDemoThree = analyticsFeatureFlagMethod("ptxSwapLiveAppDemoThree")?.enabled;
const ptxSwapThorswapProvider = analyticsFeatureFlagMethod("ptxSwapThorswapProvider")?.enabled;
const ptxSwapExodusProvider = analyticsFeatureFlagMethod("ptxSwapExodusProvider")?.enabled;
@@ -109,6 +110,7 @@ const getPtxAttributes = () => {
ptxSwapMoonpayProviderEnabled,
ptxSwapLiveAppDemoZero,
ptxSwapLiveAppDemoOne,
+ ptxSwapLiveAppDemoThree,
ptxSwapThorswapProvider,
ptxSwapExodusProvider,
};
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/App/App.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/App/App.tsx
new file mode 100644
index 000000000000..ecbb5d8de74b
--- /dev/null
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/App/App.tsx
@@ -0,0 +1,53 @@
+import { useSwapLiveConfig } from "@ledgerhq/live-common/exchange/swap/hooks/index";
+import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index";
+import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index";
+import React, { useState } from "react";
+import styled from "styled-components";
+import SwapWebView from "~/renderer/screens/exchange/Swap2/Form/SwapWebViewDemo3";
+
+const Root = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+`;
+
+// TODO: fix with proper error handling
+const ErrorWrapper = styled.div`
+ width: auto;
+ display: inline-flex;
+ align-self: center;
+ align-items: center;
+ justify-self: center;
+ justify-content: center;
+ padding: 24px;
+ border-radius: 14px;
+ background-color: rgba(255, 0, 0, 0.3);
+ color: #fff;
+ font-weight: 500;
+`;
+
+export function SwapApp() {
+ const [unavailable, setUnavailable] = useState(false);
+ const swapLiveEnabledFlag = useSwapLiveConfig();
+ const swapLiveAppManifestID = swapLiveEnabledFlag?.params?.manifest_id;
+
+ const localManifest = useLocalLiveAppManifest(swapLiveAppManifestID || undefined);
+ const remoteManifest = useRemoteLiveAppManifest(swapLiveAppManifestID || undefined);
+ const manifest = localManifest || remoteManifest;
+
+ if (!manifest) {
+ // TODO: fix with proper error handling
+ return Unable to load application: missing manifest;
+ }
+
+ if (unavailable) {
+ // TODO: fix with proper error handling
+ return Unable to load application: Unavailable;
+ }
+
+ return (
+
+ setUnavailable(true)} />
+
+ );
+}
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/App/index.ts b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/App/index.ts
new file mode 100644
index 000000000000..c85430266518
--- /dev/null
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/App/index.ts
@@ -0,0 +1 @@
+export * from "./App";
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/SectionTarget.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/SectionTarget.tsx
index 6c1f35cf3bca..29400edebe52 100644
--- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/SectionTarget.tsx
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/SectionTarget.tsx
@@ -14,7 +14,7 @@ import { context } from "~/renderer/drawers/Provider";
import { useMaybeAccountName } from "~/renderer/reducers/wallet";
import { useGetSwapTrackingProperties } from "../../utils/index";
import TargetAccountDrawer from "../TargetAccountDrawer";
-import { useIsSwapLiveFlagEnabled } from "../useIsSwapLiveFlagEnabled";
+import { useIsSwapLiveFlagEnabled } from "../../hooks/useIsSwapLiveFlagEnabled";
import SectionInformative from "./SectionInformative";
import SummaryLabel from "./SummaryLabel";
import SummarySection from "./SummarySection";
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/index.tsx
index ef586fd52986..ebc31449d6bd 100644
--- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/index.tsx
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/FormSummary/index.tsx
@@ -1,7 +1,7 @@
import { SwapTransactionType } from "@ledgerhq/live-common/exchange/swap/types";
import React from "react";
import styled from "styled-components";
-import { useIsSwapLiveFlagEnabled } from "../useIsSwapLiveFlagEnabled";
+import { useIsSwapLiveFlagEnabled } from "../../hooks/useIsSwapLiveFlagEnabled";
import SectionFees from "./SectionFees";
import SectionTarget from "./SectionTarget";
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/Migrations/SwapMigrationUI.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/Migrations/SwapMigrationUI.tsx
index ffa34267d566..3d3a6d684b6b 100644
--- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/Migrations/SwapMigrationUI.tsx
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/Migrations/SwapMigrationUI.tsx
@@ -10,7 +10,7 @@ import SwapFormRates from "../FormRates";
import SwapFormSummary from "../FormSummary";
import LoadingState from "../Rates/LoadingState";
import { SwapWebManifestIDs } from "../SwapWebView";
-import { useIsSwapLiveFlagEnabled } from "../useIsSwapLiveFlagEnabled";
+import { useIsSwapLiveFlagEnabled } from "../../hooks/useIsSwapLiveFlagEnabled";
const Button = styled(ButtonBase)`
width: 100%;
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/SwapWebView.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/SwapWebView.tsx
index 79da6cb9c09e..aa176673840d 100644
--- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/SwapWebView.tsx
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/SwapWebView.tsx
@@ -45,14 +45,14 @@ import {
} from "@ledgerhq/live-common/exchange/swap/webApp/utils";
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types";
import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
+import { useTranslation } from "react-i18next";
+import { track } from "~/renderer/analytics/segment";
import { usePTXCustomHandlers } from "~/renderer/components/WebPTXPlayer/CustomHandlers";
import { NetworkStatus, useNetworkStatus } from "~/renderer/hooks/useNetworkStatus";
import { flattenAccountsSelector } from "~/renderer/reducers/accounts";
import { captureException } from "~/sentry/renderer";
import { CustomSwapQuotesState } from "../hooks/useSwapLiveAppQuoteState";
import FeesDrawerLiveApp from "./FeesDrawerLiveApp";
-import { track } from "~/renderer/analytics/segment";
-import { useTranslation } from "react-i18next";
export class UnableToLoadSwapLiveError extends Error {
constructor(message: string) {
@@ -86,18 +86,19 @@ export type SwapProps = {
export type SwapWebProps = {
manifest: LiveAppManifest;
+ liveAppUnavailable: () => void;
+ setQuoteState?: (next: CustomSwapQuotesState) => void;
swapState?: Partial;
- liveAppUnavailable(): void;
isMaxEnabled?: boolean;
sourceCurrency?: TokenCurrency | CryptoCurrency;
targetCurrency?: TokenCurrency | CryptoCurrency;
- setQuoteState: (next: CustomSwapQuotesState) => void;
};
let lastGasOptions: GasOptions;
export const SwapWebManifestIDs = {
Demo0: "swap-live-app-demo-0",
Demo1: "swap-live-app-demo-1",
+ Demo3: "swap-live-app-demo-3",
};
const SwapWebAppWrapper = styled.div`
@@ -181,7 +182,7 @@ const SwapWebView = ({
const fromUnit = sourceCurrency?.units[0];
if (!quote.params) {
- setQuoteState({
+ setQuoteState?.({
amountTo: undefined,
swapError: undefined,
counterValue: undefined,
@@ -192,7 +193,7 @@ const SwapWebView = ({
if (quote.params?.code && fromUnit) {
switch (quote.params.code) {
case "minAmountError":
- setQuoteState({
+ setQuoteState?.({
amountTo: undefined,
counterValue: undefined,
swapError: new SwapExchangeRateAmountTooLow(undefined, {
@@ -209,7 +210,7 @@ const SwapWebView = ({
});
return Promise.resolve();
case "maxAmountError":
- setQuoteState({
+ setQuoteState?.({
amountTo: undefined,
counterValue: undefined,
swapError: new SwapExchangeRateAmountTooLow(undefined, {
@@ -236,7 +237,7 @@ const SwapWebView = ({
)
: undefined;
- setQuoteState({
+ setQuoteState?.({
amountTo,
counterValue: counterValue,
swapError: undefined,
@@ -487,7 +488,7 @@ const SwapWebView = ({
useEffect(() => {
// Determine the new quote state based on network status
- setQuoteState({
+ setQuoteState?.({
amountTo: undefined,
counterValue: undefined,
...{
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/SwapWebViewDemo3.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/SwapWebViewDemo3.tsx
new file mode 100644
index 000000000000..1fa0a376809c
--- /dev/null
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/SwapWebViewDemo3.tsx
@@ -0,0 +1,148 @@
+import { SwapLiveError } from "@ledgerhq/live-common/exchange/swap/types";
+import { LiveAppManifest } from "@ledgerhq/live-common/platform/types";
+import { handlers as loggerHandlers } from "@ledgerhq/live-common/wallet-api/CustomLogger/server";
+import { getEnv } from "@ledgerhq/live-env";
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import { useSelector } from "react-redux";
+import styled from "styled-components";
+import { Web3AppWebview } from "~/renderer/components/Web3AppWebview";
+import { initialWebviewState } from "~/renderer/components/Web3AppWebview/helpers";
+import { WebviewAPI, WebviewProps, WebviewState } from "~/renderer/components/Web3AppWebview/types";
+import { TopBar } from "~/renderer/components/WebPlatformPlayer/TopBar";
+import { usePTXCustomHandlers } from "~/renderer/components/WebPTXPlayer/CustomHandlers";
+import { context } from "~/renderer/drawers/Provider";
+import useTheme from "~/renderer/hooks/useTheme";
+import logger from "~/renderer/logger";
+import {
+ counterValueCurrencySelector,
+ enablePlatformDevToolsSelector,
+ languageSelector,
+} from "~/renderer/reducers/settings";
+import { captureException } from "~/sentry/renderer";
+import WebviewErrorDrawer from "./WebviewErrorDrawer/index";
+
+export class UnableToLoadSwapLiveError extends Error {
+ constructor(message: string) {
+ const name = "UnableToLoadSwapLiveError";
+ super(message || name);
+ this.name = name;
+ this.message = message;
+ }
+}
+
+export type SwapProps = {
+ provider: string;
+ fromAccountId: string;
+ fromParentAccountId?: string;
+ toAccountId: string;
+ fromAmount: string;
+ toAmount?: string;
+ quoteId: string;
+ rate: string;
+ feeStrategy: string;
+ customFeeConfig: string;
+ cacheKey: string;
+ loading: boolean;
+ error: boolean;
+ providerRedirectURL: string;
+ toNewTokenId: string;
+ swapApiBase: string;
+ estimatedFees: string;
+ estimatedFeesUnit: string;
+};
+
+export type SwapWebProps = {
+ manifest: LiveAppManifest;
+ liveAppUnavailable: () => void;
+};
+
+const SwapWebAppWrapper = styled.div`
+ width: 100%;
+ flex: 1;
+`;
+
+const SWAP_API_BASE = getEnv("SWAP_API_BASE");
+
+const SwapWebView = ({ manifest, liveAppUnavailable }: SwapWebProps) => {
+ const {
+ colors: {
+ palette: { type: themeType },
+ },
+ } = useTheme();
+
+ const webviewAPIRef = useRef(null);
+ const { setDrawer } = React.useContext(context);
+ const [webviewState, setWebviewState] = useState(initialWebviewState);
+ const fiatCurrency = useSelector(counterValueCurrencySelector);
+ const locale = useSelector(languageSelector);
+ const enablePlatformDevTools = useSelector(enablePlatformDevToolsSelector);
+
+ const customPTXHandlers = usePTXCustomHandlers(manifest);
+ const customHandlers = useMemo(
+ () => ({
+ ...loggerHandlers,
+ ...customPTXHandlers,
+ }),
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [],
+ );
+
+ const hashString = useMemo(() => new URLSearchParams({}).toString(), []);
+
+ const onSwapWebviewError = (error?: SwapLiveError) => {
+ console.error("onSwapWebviewError", error);
+ logger.critical(error);
+ setDrawer(WebviewErrorDrawer, error);
+ };
+
+ const onStateChange: WebviewProps["onStateChange"] = state => {
+ setWebviewState(state);
+
+ if (!state.loading && state.isAppUnavailable) {
+ liveAppUnavailable();
+ captureException(
+ new UnableToLoadSwapLiveError(
+ '"Failed to load swap live app using WebPlatformPlayer in SwapWeb",',
+ ),
+ );
+ }
+ };
+
+ useEffect(() => {
+ if (webviewState.url.includes("/unknown-error")) {
+ // the live app has re-directed to /unknown-error. Handle this in callback, probably wallet-api failure.
+ onSwapWebviewError();
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [webviewState.url]);
+
+ return (
+ <>
+ {enablePlatformDevTools && (
+
+ )}
+
+
+
+
+ >
+ );
+};
+
+export default SwapWebView;
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx
index 1d8c12ff6644..60673be70446 100644
--- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/index.tsx
@@ -37,6 +37,7 @@ import { useSwapLiveAppHook } from "~/renderer/hooks/swap-migrations/useSwapLive
import { flattenAccountsSelector, shallowAccountsSelector } from "~/renderer/reducers/accounts";
import { languageSelector } from "~/renderer/reducers/settings";
import { walletSelector } from "~/renderer/reducers/wallet";
+import { useIsSwapLiveFlagEnabled } from "../hooks/useIsSwapLiveFlagEnabled";
import { useSwapLiveAppQuoteState } from "../hooks/useSwapLiveAppQuoteState";
import { trackSwapError, useGetSwapTrackingProperties } from "../utils/index";
import ExchangeDrawer from "./ExchangeDrawer/index";
@@ -44,7 +45,6 @@ import SwapFormSelectors from "./FormSelectors";
import { SwapMigrationUI } from "./Migrations/SwapMigrationUI";
import EmptyState from "./Rates/EmptyState";
import SwapWebView, { SwapWebProps } from "./SwapWebView";
-import { useIsSwapLiveFlagEnabled } from "./useIsSwapLiveFlagEnabled";
const DAPP_PROVIDERS = ["paraswap", "oneinch", "moonpay"];
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/useIsSwapLiveFlagEnabled.ts b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/useIsSwapLiveFlagEnabled.ts
deleted file mode 100644
index cb64a7fe6214..000000000000
--- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/Form/useIsSwapLiveFlagEnabled.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
-
-// used to get the value of the Swap Live App flag
-export const useIsSwapLiveFlagEnabled = (flag: string): boolean => {
- const demoZero = useFeature("ptxSwapLiveAppDemoZero");
- const demoOne = useFeature("ptxSwapLiveAppDemoOne");
-
- if (demoZero?.enabled === demoOne?.enabled) return false;
-
- switch (flag) {
- case "ptxSwapLiveAppDemoOne":
- return Boolean(demoOne?.enabled);
- case "ptxSwapLiveAppDemoZero":
- return Boolean(demoZero?.enabled && !demoOne?.enabled);
- default:
- throw new Error(`Unknown Swap Live App flag: ${flag}`);
- }
-};
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/hooks/useIsSwapLiveFlagEnabled.ts b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/hooks/useIsSwapLiveFlagEnabled.ts
new file mode 100644
index 000000000000..9ae7f7d4f48b
--- /dev/null
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/hooks/useIsSwapLiveFlagEnabled.ts
@@ -0,0 +1,22 @@
+import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
+
+// used to get the value of the Swap Live App flag
+export const useIsSwapLiveFlagEnabled = (flag: string): boolean => {
+ const demoZero = useFeature("ptxSwapLiveAppDemoZero");
+ const demoOne = useFeature("ptxSwapLiveAppDemoOne");
+ const demoThree = useFeature("ptxSwapLiveAppDemoThree");
+
+ if (flag === "ptxSwapLiveAppDemoThree") {
+ return !!demoThree?.enabled;
+ }
+
+ if (flag === "ptxSwapLiveAppDemoOne") {
+ return !!demoOne?.enabled;
+ }
+
+ if (flag === "ptxSwapLiveAppDemoZero") {
+ return !!demoZero?.enabled;
+ }
+
+ throw new Error(`Unknown Swap Live App flag: ${flag}`);
+};
diff --git a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/index.tsx b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/index.tsx
index 03fdadc93d32..1e058217d94c 100644
--- a/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/index.tsx
+++ b/apps/ledger-live-desktop/src/renderer/screens/exchange/Swap2/index.tsx
@@ -6,6 +6,8 @@ import SwapForm from "./Form";
import SwapHistory from "./History";
import SwapNavbar from "./Navbar";
+import { SwapApp } from "./App";
+import { useIsSwapLiveFlagEnabled } from "./hooks/useIsSwapLiveFlagEnabled";
const Body = styled(Box)`
flex: 1;
`;
@@ -45,17 +47,20 @@ const GlobalStyle = createGlobalStyle`
}
`;
-function Swap2() {
+const Swap2 = () => {
+ const isDemo3Enabled = useIsSwapLiveFlagEnabled("ptxSwapLiveAppDemoThree");
+ const SwapPage = isDemo3Enabled ? SwapApp : SwapForm;
+
return (
-
+
);
-}
+};
export default Swap2;
diff --git a/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.test.ts b/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.test.ts
index c904e9eb72de..b2357beb294c 100644
--- a/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.test.ts
+++ b/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.test.ts
@@ -13,44 +13,51 @@ const useMockFeature = useFeature as jest.Mock;
describe("useSwapLiveConfig", () => {
// Setup the mock for useFeatureFlags to return an object with getFeature
- const setupFeatureFlagsMock = (demoZeroConfig, demoOneConfig) => {
- useMockFeature.mockImplementation(flagName => {
- switch (flagName) {
- case "ptxSwapLiveAppDemoZero":
- return demoZeroConfig;
- case "ptxSwapLiveAppDemoOne":
- return demoOneConfig;
- default:
- return null;
- }
- });
+ const setupFeatureFlagsMock = (
+ flags: Partial<{ enabled: boolean; params: { manifest_id: string } }>[],
+ ) => {
+ const flagsKeys = [
+ "ptxSwapLiveAppDemoZero",
+ "ptxSwapLiveAppDemoOne",
+ "ptxSwapLiveAppDemoThree",
+ ];
+
+ useMockFeature.mockImplementation(flagName => flags[flagsKeys.indexOf(flagName)] ?? null);
};
afterEach(() => {
jest.clearAllMocks();
});
- it("should return null if both features have the same enabled state", () => {
- setupFeatureFlagsMock({ enabled: true }, { enabled: true });
+ it("should highest priority flag if all features have the same enabled state", () => {
+ setupFeatureFlagsMock([
+ { enabled: true, params: { manifest_id: "demo_0" } },
+ { enabled: true, params: { manifest_id: "demo_1" } },
+ { enabled: true, params: { manifest_id: "demo_3" } },
+ ]);
const { result } = renderHook(() => useSwapLiveConfig());
- expect(result.current).toBeNull();
+ expect(result.current).not.toBeNull();
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ expect((result.current?.params as any)?.manifest_id).toEqual("demo_3");
});
it("should return null if both features are disabled", () => {
- setupFeatureFlagsMock({ enabled: false }, { enabled: false });
+ setupFeatureFlagsMock([{ enabled: false }, { enabled: false }, { enabled: false }]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toBeNull();
});
it("should return demoZero if only demoZero is enabled", () => {
- setupFeatureFlagsMock(
+ setupFeatureFlagsMock([
{
enabled: true,
params: { manifest_id: "swap-live-app-demo-0" },
},
{ enabled: false },
- );
+ { enabled: false },
+ ]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual({
enabled: true,
@@ -59,17 +66,35 @@ describe("useSwapLiveConfig", () => {
});
it("should return demoOne if only demoOne is enabled", () => {
- setupFeatureFlagsMock(
+ setupFeatureFlagsMock([
{ enabled: false },
{
enabled: true,
params: { manifest_id: "swap-live-app-demo-1" },
},
- );
+ { enabled: false },
+ ]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual({
enabled: true,
params: { manifest_id: "swap-live-app-demo-1" },
});
});
+
+ it("should return demoThree if only demoOne is enabled", () => {
+ setupFeatureFlagsMock([
+ { enabled: false },
+ {
+ enabled: true,
+ params: { manifest_id: "swap-live-app-demo-3" },
+ },
+ { enabled: false },
+ ]);
+
+ const { result } = renderHook(() => useSwapLiveConfig());
+ expect(result.current).toEqual({
+ enabled: true,
+ params: { manifest_id: "swap-live-app-demo-3" },
+ });
+ });
});
diff --git a/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.ts b/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.ts
index b02089d96425..6cf9ec52c24f 100644
--- a/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.ts
+++ b/libs/ledger-live-common/src/exchange/swap/hooks/live-app-migration/useSwapLiveConfig.ts
@@ -4,8 +4,11 @@ import { useFeature } from "../../../../featureFlags";
export function useSwapLiveConfig() {
const demoZero = useFeature("ptxSwapLiveAppDemoZero");
const demoOne = useFeature("ptxSwapLiveAppDemoOne");
+ const demoThree = useFeature("ptxSwapLiveAppDemoThree");
- if (demoZero?.enabled === demoOne?.enabled) return null;
+ // Order is important in order to get the first enabled flag
+ const flags = [demoThree, demoOne, demoZero];
+ const enabledFlag = flags.find(flag => flag?.enabled);
- return demoZero?.enabled && !demoOne?.enabled ? demoZero : demoOne?.enabled ? demoOne : null;
+ return enabledFlag ?? null;
}
diff --git a/libs/ledger-live-common/src/featureFlags/defaultFeatures.ts b/libs/ledger-live-common/src/featureFlags/defaultFeatures.ts
index 7f281c759260..4e5ebf851286 100644
--- a/libs/ledger-live-common/src/featureFlags/defaultFeatures.ts
+++ b/libs/ledger-live-common/src/featureFlags/defaultFeatures.ts
@@ -406,6 +406,13 @@ export const DEFAULT_FEATURES: Features = {
},
},
+ ptxSwapLiveAppDemoThree: {
+ enabled: false,
+ params: {
+ manifest_id: "swap-live-app-demo-3",
+ },
+ },
+
ptxSwapMoonpayProvider: DEFAULT_FEATURE,
ptxSwapExodusProvider: DEFAULT_FEATURE,
ptxSwapThorswapProvider: DEFAULT_FEATURE,
diff --git a/libs/ledgerjs/packages/types-live/src/feature.ts b/libs/ledgerjs/packages/types-live/src/feature.ts
index cc5c69b93a67..b1ebcd158956 100644
--- a/libs/ledgerjs/packages/types-live/src/feature.ts
+++ b/libs/ledgerjs/packages/types-live/src/feature.ts
@@ -164,6 +164,7 @@ export type Features = CurrencyFeatures & {
fetchAdditionalCoins: Feature_FetchAdditionalCoins;
ptxSwapLiveAppDemoZero: Feature_PtxSwapLiveAppDemoZero;
ptxSwapLiveAppDemoOne: Feature_PtxSwapLiveAppDemoZero;
+ ptxSwapLiveAppDemoThree: Feature_PtxSwapLiveAppDemoZero;
ptxSwapMoonpayProvider: Feature_PtxSwapMoonpayProvider;
ptxSwapExodusProvider: Feature_PtxSwapExodusProvider;
ptxSwapThorswapProvider: Feature_PtxSwapThorswapProvider;