Skip to content

Commit

Permalink
feat: add ptxSwapCoreExperiment
Browse files Browse the repository at this point in the history
feat: 2

chore: 3

chore: another build

chore: another build

chore: another build

chore: another build

chore changeset
  • Loading branch information
liviuciulinaru committed Oct 10, 2024
1 parent b961229 commit faa6068
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 39 deletions.
7 changes: 7 additions & 0 deletions .changeset/friendly-doors-ring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@ledgerhq/types-live": patch
"ledger-live-desktop": patch
"@ledgerhq/live-common": patch
---

add ptxSwapCoreExperiment
46 changes: 25 additions & 21 deletions apps/ledger-live-desktop/src/renderer/analytics/segment.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import { v4 as uuid } from "uuid";
import { runOnceWhen } from "@ledgerhq/live-common/utils/runOnceWhen";
import { getEnv } from "@ledgerhq/live-env";
import {
GENESIS_PASS_COLLECTION_CONTRACT,
hasNftInAccounts,
INFINITY_PASS_COLLECTION_CONTRACT,
} from "@ledgerhq/live-nft";
import { getDefaultAccountName } from "@ledgerhq/live-wallet/accountName";
import { AccountLike, Feature, FeatureId, Features, idsToLanguage } from "@ledgerhq/types-live";
import invariant from "invariant";
import { useCallback, useContext } from "react";
import { ReplaySubject } from "rxjs";
import { getEnv } from "@ledgerhq/live-env";
import logger from "~/renderer/logger";
import { v4 as uuid } from "uuid";
import { getParsedSystemLocale } from "~/helpers/systemLocale";
import user from "~/helpers/user";
import { runOnceWhen } from "@ledgerhq/live-common/utils/runOnceWhen";
import logger from "~/renderer/logger";
import { State } from "~/renderer/reducers";
import {
sidebarCollapsedSelector,
shareAnalyticsSelector,
developerModeSelector,
devicesModelListSelector,
hasSeenAnalyticsOptInPromptSelector,
languageSelector,
lastSeenDeviceSelector,
localeSelector,
languageSelector,
devicesModelListSelector,
shareAnalyticsSelector,
sharePersonalizedRecommendationsSelector,
hasSeenAnalyticsOptInPromptSelector,
sidebarCollapsedSelector,
trackingEnabledSelector,
developerModeSelector,
} from "~/renderer/reducers/settings";
import { State } from "~/renderer/reducers";
import { AccountLike, Feature, FeatureId, Features, idsToLanguage } from "@ledgerhq/types-live";
import { accountsSelector } from "../reducers/accounts";
import {
GENESIS_PASS_COLLECTION_CONTRACT,
hasNftInAccounts,
INFINITY_PASS_COLLECTION_CONTRACT,
} from "@ledgerhq/live-nft";
import createStore from "../createStore";
import { currentRouteNameRef, previousRouteNameRef } from "./screenRefs";
import { useCallback, useContext } from "react";
import { analyticsDrawerContext } from "../drawers/Provider";
import { getDefaultAccountName } from "@ledgerhq/live-wallet/accountName";
import { accountsSelector } from "../reducers/accounts";
import { currentRouteNameRef, previousRouteNameRef } from "./screenRefs";

invariant(typeof window !== "undefined", "analytics/segment must be called on renderer thread");
// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand Down Expand Up @@ -89,6 +89,9 @@ const getPtxAttributes = () => {
const ptxSwapLiveAppDemoThree = analyticsFeatureFlagMethod("ptxSwapLiveAppDemoThree")?.enabled;
const ptxSwapThorswapProvider = analyticsFeatureFlagMethod("ptxSwapThorswapProvider")?.enabled;
const ptxSwapExodusProvider = analyticsFeatureFlagMethod("ptxSwapExodusProvider")?.enabled;
const ptxSwapCoreExperimentFlag = analyticsFeatureFlagMethod("ptxSwapCoreExperiment");
const ptxSwapCoreExperiment =
ptxSwapCoreExperimentFlag?.enabled && ptxSwapCoreExperimentFlag?.params?.variant;

const isBatch1Enabled: boolean =
!!fetchAdditionalCoins?.enabled && fetchAdditionalCoins?.params?.batch === 1;
Expand All @@ -115,6 +118,7 @@ const getPtxAttributes = () => {
ptxSwapLiveAppDemoThree,
ptxSwapThorswapProvider,
ptxSwapExodusProvider,
ptxSwapCoreExperiment,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { NetworkDown } from "@ledgerhq/errors";
import { getAccountBridge } from "@ledgerhq/live-common/bridge/impl";
import { formatCurrencyUnit } from "@ledgerhq/live-common/currencies/index";
import { SwapExchangeRateAmountTooLow } from "@ledgerhq/live-common/errors";
import { useSwapLiveConfig } from "@ledgerhq/live-common/exchange/swap/hooks/index";
import { getAbandonSeedAddress } from "@ledgerhq/live-common/exchange/swap/hooks/useFromState";
import { SwapLiveError } from "@ledgerhq/live-common/exchange/swap/types";
import {
Expand Down Expand Up @@ -136,6 +137,7 @@ const SwapWebView = ({
const { networkStatus } = useNetworkStatus();
const isOffline = networkStatus === NetworkStatus.OFFLINE;
const swapDefaultTrack = useGetSwapTrackingProperties();
const swapLiveEnabledFlag = useSwapLiveConfig();

const hasSwapState = !!swapState;
const customPTXHandlers = usePTXCustomHandlers(manifest);
Expand Down Expand Up @@ -471,6 +473,11 @@ const SwapWebView = ({
toNewTokenId: swapState?.toNewTokenId,
feeStrategy: swapState?.feeStrategy,
customFeeConfig: swapState?.customFeeConfig,
...(swapLiveEnabledFlag?.params &&
"variant" in swapLiveEnabledFlag.params &&
swapLiveEnabledFlag.params.variant === "Demo0"
? { ptxSwapCoreExperiment: "Demo0" }
: {}),
};

Object.entries(swapParams).forEach(([key, value]) => {
Expand All @@ -489,6 +496,7 @@ const SwapWebView = ({
isMaxEnabled,
fromCurrency,
targetCurrency?.id,
swapLiveEnabledFlag,
]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import WebviewErrorDrawer from "./WebviewErrorDrawer/index";
import { GasOptions } from "@ledgerhq/coin-evm/lib/types/transaction";
import { getMainAccount, getParentAccount } from "@ledgerhq/live-common/account/helpers";
import { getAccountBridge } from "@ledgerhq/live-common/bridge/impl";
import { useSwapLiveConfig } from "@ledgerhq/live-common/exchange/swap/hooks/index";
import { getAbandonSeedAddress } from "@ledgerhq/live-common/exchange/swap/hooks/useFromState";
import {
convertToAtomicUnit,
Expand Down Expand Up @@ -123,6 +124,7 @@ const SwapWebView = ({ manifest, liveAppUnavailable }: SwapWebProps) => {
from?: string;
}>();
const redirectToHistory = useRedirectToSwapHistory();
const swapLiveEnabledFlag = useSwapLiveConfig();

const { networkStatus } = useNetworkStatus();
const isOffline = networkStatus === NetworkStatus.OFFLINE;
Expand Down Expand Up @@ -292,9 +294,21 @@ const SwapWebView = ({ manifest, liveAppUnavailable }: SwapWebProps) => {
).id,
}
: {}),
...(state?.from ? { fromPath: simplifyFromPath(state.from) } : {}),
...(state?.from ? { fromPath: simplifyFromPath(state?.from) } : {}),
...(swapLiveEnabledFlag?.params && "variant" in swapLiveEnabledFlag.params
? {
ptxSwapCoreExperiment: swapLiveEnabledFlag.params?.variant as string,
}
: {}),
}).toString(),
[isOffline, state?.defaultAccount, state?.defaultParentAccount, walletState, state?.from],
[
isOffline,
state?.defaultAccount,
state?.defaultParentAccount,
walletState,
state?.from,
swapLiveEnabledFlag,
],
);

const onSwapWebviewError = (error?: SwapLiveError) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ 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");
const coreExperiment = useFeature("ptxSwapCoreExperiment");
const coreExperimentVariant = coreExperiment?.enabled && coreExperiment?.params?.variant;

if (flag === "ptxSwapLiveAppDemoThree") {
return !!demoThree?.enabled;
return (
!!demoThree?.enabled || ["Demo3", "Demo3Thorswap"].includes(coreExperimentVariant as string)
);
}

if (flag === "ptxSwapLiveAppDemoOne") {
return !!demoOne?.enabled;
if (flag === "ptxSwapLiveAppDemoZero") {
return !!demoZero?.enabled || coreExperimentVariant === "Demo0";
}

if (flag === "ptxSwapLiveAppDemoZero") {
return !!demoZero?.enabled;
if (flag === "ptxSwapLiveAppDemoOne") {
return false;
}

throw new Error(`Unknown Swap Live App flag: ${flag}`);
throw new Error(`Unknown Swap Live App flag ${flag}`);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ const useMockFeature = useFeature as jest.Mock;
describe("useSwapLiveConfig", () => {
// Setup the mock for useFeatureFlags to return an object with getFeature
const setupFeatureFlagsMock = (
flags: Partial<{ enabled: boolean; params: { manifest_id: string } }>[],
flags: Partial<
{ enabled: boolean; params: { manifest_id: string; variant?: string } } | undefined
>[],
) => {
const flagsKeys = [
"ptxSwapLiveAppDemoZero",
"ptxSwapLiveAppDemoOne",
"ptxSwapLiveAppDemoThree",
"ptxSwapCoreExperiment",
];

useMockFeature.mockImplementation(flagName => flags[flagsKeys.indexOf(flagName)] ?? null);
Expand All @@ -40,7 +43,7 @@ describe("useSwapLiveConfig", () => {
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");
expect((result.current?.params as any)?.manifest_id).toEqual("demo_0");
});

it("should return null if both features are disabled", () => {
Expand Down Expand Up @@ -97,4 +100,99 @@ describe("useSwapLiveConfig", () => {
params: { manifest_id: "swap-live-app-demo-3" },
});
});

it("should return config when ptxSwapCoreExperiment has valid variant Demo0", () => {
const expected = {
enabled: true,
params: { manifest_id: "swap-live-app-demo-0", variant: "Demo0" },
};
setupFeatureFlagsMock([{ enabled: false }, { enabled: false }, { enabled: false }, expected]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual(expected);
});

it("should return config when ptxSwapCoreExperiment has valid variant Demo3", () => {
setupFeatureFlagsMock([
{ enabled: false },
{ enabled: false },
{ enabled: false },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-3", variant: "Demo3" } },
]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual({
enabled: true,
params: { manifest_id: "swap-live-app-demo-3", variant: "Demo3" },
});
});

it("should return config when ptxSwapCoreExperiment has valid variant Demo3Thorswap", () => {
setupFeatureFlagsMock([
{ enabled: false },
{ enabled: false },
{ enabled: false },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-3", variant: "Demo3Thorswap" } },
]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual({
enabled: true,
params: { manifest_id: "swap-live-app-demo-3", variant: "Demo3Thorswap" },
});
});

it("should return demoZero if demoThree and are enabled", () => {
setupFeatureFlagsMock([
{ enabled: true, params: { manifest_id: "swap-live-app-demo-0" } },
{ enabled: false },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-3" } },
]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual({
enabled: true,
params: { manifest_id: "swap-live-app-demo-0" },
});
});

it("should return demoZero if all demo flags are enabled", () => {
setupFeatureFlagsMock([
{ enabled: true, params: { manifest_id: "swap-live-app-demo-0" } },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-1" } },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-3" } },
]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual({
enabled: true,
params: { manifest_id: "swap-live-app-demo-0" },
});
});

it("should prioritize demoZero over demo flags if all are enabled", () => {
setupFeatureFlagsMock([
{ enabled: true, params: { manifest_id: "swap-live-app-demo-0" } },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-1" } },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-3" } },
{ enabled: true, params: { manifest_id: "swap-live-app-demo-3", variant: "Demo3" } },
]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toEqual({
enabled: true,
params: { manifest_id: "swap-live-app-demo-0" },
});
});

it("should return null when coreExperiment is enabled but has no variant", () => {
setupFeatureFlagsMock([
{ enabled: false },
{ enabled: false },
{ enabled: false },
{ enabled: true, params: { manifest_id: "core-experiment" } },
]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toBeNull();
});

it("should return null when all flags are undefined", () => {
setupFeatureFlagsMock([undefined, undefined, undefined, undefined]);
const { result } = renderHook(() => useSwapLiveConfig());
expect(result.current).toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
import { Feature_PtxSwapCoreExperiment } from "@ledgerhq/types-live";
import { useFeature } from "../../../../featureFlags";

// check if the variant is valid for core rollout experiment
export type CoreExperimentParams = NonNullable<Feature_PtxSwapCoreExperiment["params"]>;
export type ValidVariant = CoreExperimentParams["variant"];

// used to enable the Swap Live App globally
export function useSwapLiveConfig() {
const demoZero = useFeature("ptxSwapLiveAppDemoZero");
const demoOne = useFeature("ptxSwapLiveAppDemoOne");
const demoThree = useFeature("ptxSwapLiveAppDemoThree");
const demoOne = useFeature("ptxSwapLiveAppDemoOne");
const demoZero = useFeature("ptxSwapLiveAppDemoZero");
const coreExperiment = useFeature("ptxSwapCoreExperiment");

if (demoZero?.enabled) {
return demoZero;
}

if (demoThree?.enabled) {
return demoThree;
}

if (coreExperiment?.enabled) {
const variant = coreExperiment?.params?.variant;
if (!variant || !(variant satisfies ValidVariant)) {
return null;
}
return coreExperiment;
}

// Order is important in order to get the first enabled flag
const flags = [demoThree, demoOne, demoZero];
const enabledFlag = flags.find(flag => flag?.enabled);
if (demoOne?.enabled) {
return demoOne;
}

return enabledFlag ?? null;
return null;
}
10 changes: 9 additions & 1 deletion libs/ledger-live-common/src/featureFlags/defaultFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
Features,
} from "@ledgerhq/types-live";
import reduce from "lodash/reduce";
import { formatToFirebaseFeatureId } from "./firebaseFeatureFlags";
import { BUY_SELL_UI_APP_ID } from "../wallet-api/constants";
import { formatToFirebaseFeatureId } from "./firebaseFeatureFlags";

/**
* Default disabled feature.
Expand Down Expand Up @@ -416,6 +416,14 @@ export const DEFAULT_FEATURES: Features = {
},
},

ptxSwapCoreExperiment: {
enabled: false,
params: {
variant: "Demo0",
manifest_id: "swap-live-app-demo-0",
},
},

ptxSwapMoonpayProvider: DEFAULT_FEATURE,
ptxSwapExodusProvider: DEFAULT_FEATURE,
ptxSwapThorswapProvider: DEFAULT_FEATURE,
Expand Down
Loading

0 comments on commit faa6068

Please sign in to comment.