Skip to content

Commit

Permalink
Merge pull request #7624 from LedgerHQ/bugfix/web3hub-request
Browse files Browse the repository at this point in the history
fix: missing query params for web3hub manifests requests [LIVE-13726]
  • Loading branch information
Justkant authored Aug 20, 2024
2 parents 8ff87b2 + 3348e3f commit 4161188
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-candles-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"live-mobile": patch
---

fix: missing query params for web3hub manifests requests
2 changes: 1 addition & 1 deletion apps/ledger-live-mobile/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 36176158
versionName "3.40.1"
versionName "3.48.0"
resValue "string", "build_config_package", "com.ledger.live"
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
Expand Down
2 changes: 1 addition & 1 deletion apps/ledger-live-mobile/ios/ledgerlivemobile/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.40.1</string>
<string>3.48.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
import { useInfiniteQuery } from "@tanstack/react-query";
import useEnv from "@ledgerhq/live-common/hooks/useEnv";
import {
fetchManifests,
selectManifests,
getNextPageParam,
fetchManifestsMock,
} from "LLM/features/Web3Hub/utils/api/manifests";
import { useLocale } from "~/context/Locale";

export const queryKey = (selectedCategory: string) => ["web3hub/manifests", selectedCategory];
export const queryKey = (
selectedCategory: string,
isExperimentalAppEnabled: boolean,
isDebugAppEnabled: boolean,
locale: string,
) => [
"web3hub/manifests",
selectedCategory,
isExperimentalAppEnabled ? "exp-on" : "exp-off",
isDebugAppEnabled ? "debug-on" : "debug-off",
locale,
];

const isInTest = process.env.NODE_ENV === "test" || !!process.env.MOCK_WEB3HUB;
const queryFn = isInTest ? fetchManifestsMock : fetchManifests;

export default function useManifestListViewModel(selectedCategory: string) {
const isExperimentalAppEnabled = useEnv<"PLATFORM_EXPERIMENTAL_APPS">(
"PLATFORM_EXPERIMENTAL_APPS",
) as boolean;
const isDebugAppEnabled = useEnv<"PLATFORM_DEBUG">("PLATFORM_DEBUG") as boolean;
const { locale } = useLocale();

const manifestsQuery = useInfiniteQuery({
queryKey: queryKey(selectedCategory),
queryFn: queryFn(selectedCategory, ""),
queryKey: queryKey(selectedCategory, isExperimentalAppEnabled, isDebugAppEnabled, locale),
queryFn: queryFn(selectedCategory, "", isExperimentalAppEnabled, isDebugAppEnabled, locale),
initialPageParam: 1,
getNextPageParam,
select: selectManifests,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { useQuery } from "@tanstack/react-query";
import { fetchManifestById, fetchManifestByIdMock } from "LLM/features/Web3Hub/utils/api/manifests";
import { useLocale } from "~/context/Locale";

export const queryKey = (manifestId: string) => ["web3hub/manifest", manifestId];
export const queryKey = (manifestId: string, locale: string) => [
"web3hub/manifest",
manifestId,
locale,
];

const isInTest = process.env.NODE_ENV === "test" || !!process.env.MOCK_WEB3HUB;
const queryFn = isInTest ? fetchManifestByIdMock : fetchManifestById;

export default function useWeb3HubAppViewModel(manifestId: string) {
const { locale } = useLocale();

const manifestQuery = useQuery({
queryKey: queryKey(manifestId),
queryFn: queryFn(manifestId),
queryKey: queryKey(manifestId, locale),
queryFn: queryFn(manifestId, locale),
});

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useTheme } from "@react-navigation/native";
import { SharedValue } from "react-native-reanimated";
import { Flex, Text } from "@ledgerhq/native-ui";
import { useQueryClient } from "@tanstack/react-query";
import type { MainProps } from "LLM/features/Web3Hub/types";
import { queryKey } from "LLM/features/Web3Hub/components/ManifestsList/useManifestsListViewModel";
import AnimatedBar from "LLM/features/Web3Hub/components/AnimatedBar";
import TabButton from "LLM/features/Web3Hub/components/TabButton";
import TextInput from "~/components/TextInput";
Expand Down Expand Up @@ -35,13 +33,6 @@ export default function Web3HubMainHeader({ title, navigation, layoutY }: Props)
});
}, [navigation]);

// TODO remove later
// Useful for testing the infinite loading and onEndReach working correctly
const queryClient = useQueryClient();
const clearCache = useCallback(() => {
queryClient.resetQueries({ queryKey: queryKey("all") });
}, [queryClient]);

return (
<AnimatedBar
pt={insets.top}
Expand All @@ -52,11 +43,9 @@ export default function Web3HubMainHeader({ title, navigation, layoutY }: Props)
opacityHeight={TITLE_HEIGHT}
totalHeight={TOTAL_HEADER_HEIGHT}
opacityChildren={
<TouchableOpacity onPress={clearCache}>
<Text mt={5} mb={2} numberOfLines={1} variant="h4" mx={5} accessibilityRole="header">
{title}
</Text>
</TouchableOpacity>
<Text mt={5} mb={2} numberOfLines={1} variant="h4" mx={5} accessibilityRole="header">
{title}
</Text>
}
>
<Flex height={SEARCH_HEIGHT} ml={5} flexDirection="row" alignItems="center">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import { useMemo } from "react";
import { useInfiniteQuery } from "@tanstack/react-query";
import useEnv from "@ledgerhq/live-common/hooks/useEnv";
import {
fetchManifests,
selectManifests,
getNextPageParam,
fetchManifestsMock,
} from "LLM/features/Web3Hub/utils/api/manifests";
import { useLocale } from "~/context/Locale";

export const queryKey = (search: string) => ["web3hub/manifests/search", search];
export const queryKey = (
search: string,
isExperimentalAppEnabled: boolean,
isDebugAppEnabled: boolean,
locale: string,
) => [
"web3hub/manifests/search",
search,
isExperimentalAppEnabled ? "exp-on" : "exp-off",
isDebugAppEnabled ? "debug-on" : "debug-off",
locale,
];

const isInTest = process.env.NODE_ENV === "test" || !!process.env.MOCK_WEB3HUB;
const queryFn = isInTest ? fetchManifestsMock : fetchManifests;

export default function useSearchListViewModel(search: string) {
const trimmedSearch = useMemo(() => {
return search.trim();
}, [search]);

const isExperimentalAppEnabled = useEnv<"PLATFORM_EXPERIMENTAL_APPS">(
"PLATFORM_EXPERIMENTAL_APPS",
) as boolean;
const isDebugAppEnabled = useEnv<"PLATFORM_DEBUG">("PLATFORM_DEBUG") as boolean;
const { locale } = useLocale();

const manifestsQuery = useInfiniteQuery({
queryKey: queryKey(search),
queryFn: queryFn("", search),
queryKey: queryKey(trimmedSearch, isExperimentalAppEnabled, isDebugAppEnabled, locale),
queryFn: queryFn("", trimmedSearch, isExperimentalAppEnabled, isDebugAppEnabled, locale),
initialPageParam: 1,
getNextPageParam,
select: selectManifests,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Platform } from "react-native";
import VersionNumber from "react-native-version-number";
import network from "@ledgerhq/live-network/network";
import { GetNextPageParamFunction, InfiniteData, QueryFunction } from "@tanstack/react-query";
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types";
Expand Down Expand Up @@ -31,28 +33,65 @@ export const fetchManifestsMock: (
return list.slice((pageParam - 1) * PAGE_SIZE, pageParam * PAGE_SIZE);
};

export const fetchManifests: (
const PLATFORM = Platform.OS === "ios" ? "ios" : "android";
const LLVersion = VersionNumber.appVersion;

const apiVersions = ["1.0.0", "2.0.0"];

const defaultBranches = ["stable", "soon"];

type FetchManifests = (
category: string,
search: string,
) => QueryFunction<LiveAppManifest[], string[], number> =
(category, search) =>
async ({ pageParam }) => {
const url = new URL(`${URL_ORIGIN}/api/v2/apps`);
url.searchParams.set("resultsPerPage", `${PAGE_SIZE}`);
url.searchParams.set("page", `${pageParam}`);
if (category !== "all") {
url.searchParams.set("categories", category);
}
// TODO: make sure to trim search
if (search) {
url.searchParams.set("search", search);
allowExperimentalApps: boolean,
allowDebugApps: boolean,
lang: string,
) => QueryFunction<LiveAppManifest[], string[], number>;

export const fetchManifests: FetchManifests = (
category,
search,
allowExperimentalApps,
allowDebugApps,
lang,
) => {
const url = new URL(`${URL_ORIGIN}/api/v2/apps`);
url.searchParams.set("llVersion", LLVersion);
url.searchParams.set("platform", PLATFORM);
url.searchParams.set("private", "false");
url.searchParams.set("lang", lang ? lang : "en");
apiVersions.forEach(apiVersion => {
url.searchParams.append("apiVersion", apiVersion);
});
const branches = [
...defaultBranches,
allowExperimentalApps && "experimental",
allowDebugApps && "debug",
];
branches.forEach(branch => {
if (branch) {
url.searchParams.append("branches", branch);
}
});

url.searchParams.set("resultsPerPage", `${PAGE_SIZE}`);
if (category !== "all") {
url.searchParams.set("categories", category);
}
// TODO: make sure to trim search
if (search) {
url.searchParams.set("search", search);
}

return async ({ pageParam }) => {
url.searchParams.set("page", `${pageParam}`);

const res = await network<LiveAppManifest[]>({
url: url.toString(),
});
return res.data;
};
};

export const selectManifests = (data: InfiniteData<LiveAppManifest[], number>) => {
return data.pages.flat(1);
Expand All @@ -78,11 +117,16 @@ export const fetchManifestByIdMock = (manifestId: string) => async () => {
return manifests.find(mock => mock.id === manifestId);
};

export const fetchManifestById = (manifestId: string) => async () => {
export const fetchManifestById = (manifestId: string, lang: string) => {
const url = new URL(`${URL_ORIGIN}/api/v2/apps/${manifestId}`);
url.searchParams.set("llVersion", LLVersion);
url.searchParams.set("platform", PLATFORM);
url.searchParams.set("lang", lang ? lang : "en");

const res = await network<LiveAppManifest>({
url: url.toString(),
});
return res.data;
return async () => {
const res = await network<LiveAppManifest>({
url: url.toString(),
});
return res.data;
};
};

1 comment on commit 4161188

@github-actions
Copy link

Choose a reason for hiding this comment

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

[Bot] Evm on Staging 💰 3 miss funds ($0.00) ⏲ 29s

💰 3 specs may miss funds: Ethereum, Ethereum Sepolia, Ethereum Holesky

What is the bot and how does it work? Everything is documented here!

⚠️ 2 spec hints
  • Spec Ethereum:
    • There are not enough accounts (1) to cover all mutations (3).
      Please increase the account target to at least 4 accounts
  • Spec Ethereum Holesky:
    • There are not enough accounts (1) to cover all mutations (3).
      Please increase the account target to at least 4 accounts
Details of the 0 mutations

Spec Ethereum (failed)

Spec Ethereum found 1 Ethereum accounts (preload: 178ms). Will use Ethereum 1.10.3 on nanoS 2.1.0
undefined: 0 ETH (0ops) (0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D on 44'/60'/0'/0/0) #0 js:2:ethereum:0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D:

This SEED does not have Ethereum. Please send funds to 0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D

Spec Ethereum Sepolia (10)

Spec Ethereum Sepolia found 10 Ethereum Sepolia accounts (preload: 121ms). Will use Ethereum 1.10.3 on nanoS 2.1.0
undefined: 0 𝚝ETH (2ops) (0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D on 44'/60'/0'/0/0) #0 js:2:ethereum_sepolia:0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D:
undefined: 0 𝚝ETH (2ops) (0xD37d9276401C79C2800DEbb0d6E7F9AAC76De6C0 on 44'/60'/1'/0/0) #1 js:2:ethereum_sepolia:0xD37d9276401C79C2800DEbb0d6E7F9AAC76De6C0:
undefined: 0 𝚝ETH (3ops) (0x208A8F6F1c9eeAb3C65ab00d40c2f2C3e349c7C6 on 44'/60'/2'/0/0) #2 js:2:ethereum_sepolia:0x208A8F6F1c9eeAb3C65ab00d40c2f2C3e349c7C6:
undefined: 0 𝚝ETH (1ops) (0x999bC403c86A84863Ec50Ea76bea82c0c8dE369E on 44'/60'/3'/0/0) #3 js:2:ethereum_sepolia:0x999bC403c86A84863Ec50Ea76bea82c0c8dE369E:
undefined: 0 𝚝ETH (3ops) (0x9B416F834722cb90AcC7AB8f7E1C5Da4530c3Cd1 on 44'/60'/4'/0/0) #4 js:2:ethereum_sepolia:0x9B416F834722cb90AcC7AB8f7E1C5Da4530c3Cd1:
undefined: 0 𝚝ETH (2ops) (0x414386B517CB75810015c65B2f02218b16c130a4 on 44'/60'/5'/0/0) #5 js:2:ethereum_sepolia:0x414386B517CB75810015c65B2f02218b16c130a4:
undefined: 0 𝚝ETH (2ops) (0xAa744AB24B2381240327765D0198a19287B6003A on 44'/60'/6'/0/0) #6 js:2:ethereum_sepolia:0xAa744AB24B2381240327765D0198a19287B6003A:
undefined: 0 𝚝ETH (3ops) (0xC16D418BD9eA367847CCfbC696666c96BEa02781 on 44'/60'/7'/0/0) #7 js:2:ethereum_sepolia:0xC16D418BD9eA367847CCfbC696666c96BEa02781:
undefined: 0 𝚝ETH (3ops) (0x3FD46F0F514302dE5C1314257440d49dfaD25D39 on 44'/60'/8'/0/0) #8 js:2:ethereum_sepolia:0x3FD46F0F514302dE5C1314257440d49dfaD25D39:
undefined: 0 𝚝ETH (0ops) (0xf1d93dB3e9D80478d81eE04924984AeA18808E72 on 44'/60'/9'/0/0) #9 js:2:ethereum_sepolia:0xf1d93dB3e9D80478d81eE04924984AeA18808E72:

Spec Ethereum Holesky (failed)

Spec Ethereum Holesky found 1 Ethereum Holesky accounts (preload: 127ms). Will use Ethereum 1.10.3 on nanoS 2.1.0
undefined: 0 𝚝ETH (0ops) (0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D on 44'/60'/0'/0/0) #0 js:2:ethereum_holesky:0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D:

This SEED does not have Ethereum Holesky. Please send funds to 0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D

Details of the 9 uncovered mutations

Spec Ethereum (3)

  • move 50%:
  • send max:
  • move some ERC20 like (ERC20, BEP20, etc...):

Spec Ethereum Sepolia (3)

  • move 50%: ethereum_sepolia balance is too low (10)
  • send max: ethereum_sepolia balance is too low (10)
  • move some ERC20 like (ERC20, BEP20, etc...): ethereum_sepolia balance is too low (10)

Spec Ethereum Holesky (3)

  • move 50%:
  • send max:
  • move some ERC20 like (ERC20, BEP20, etc...):
Portfolio ($0.00) – Details of the 3 currencies
Spec (accounts) State Remaining Runs (est) funds?
Ethereum (1) 0 ops , 0 ETH ($0.00) 0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D
Ethereum Sepolia (10) 21 ops , 0 𝚝ETH ($0.00) ⚠️ 0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D
Ethereum Holesky (1) 0 ops , 0 𝚝ETH ($0.00) 0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D
undefined: 0 ETH (0ops) (0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D on 44'/60'/0'/0/0) #0 js:2:ethereum:0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D:
undefined: 0 𝚝ETH (2ops) (0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D on 44'/60'/0'/0/0) #0 js:2:ethereum_sepolia:0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D:
undefined: 0 𝚝ETH (2ops) (0xD37d9276401C79C2800DEbb0d6E7F9AAC76De6C0 on 44'/60'/1'/0/0) #1 js:2:ethereum_sepolia:0xD37d9276401C79C2800DEbb0d6E7F9AAC76De6C0:
undefined: 0 𝚝ETH (3ops) (0x208A8F6F1c9eeAb3C65ab00d40c2f2C3e349c7C6 on 44'/60'/2'/0/0) #2 js:2:ethereum_sepolia:0x208A8F6F1c9eeAb3C65ab00d40c2f2C3e349c7C6:
undefined: 0 𝚝ETH (1ops) (0x999bC403c86A84863Ec50Ea76bea82c0c8dE369E on 44'/60'/3'/0/0) #3 js:2:ethereum_sepolia:0x999bC403c86A84863Ec50Ea76bea82c0c8dE369E:
undefined: 0 𝚝ETH (3ops) (0x9B416F834722cb90AcC7AB8f7E1C5Da4530c3Cd1 on 44'/60'/4'/0/0) #4 js:2:ethereum_sepolia:0x9B416F834722cb90AcC7AB8f7E1C5Da4530c3Cd1:
undefined: 0 𝚝ETH (2ops) (0x414386B517CB75810015c65B2f02218b16c130a4 on 44'/60'/5'/0/0) #5 js:2:ethereum_sepolia:0x414386B517CB75810015c65B2f02218b16c130a4:
undefined: 0 𝚝ETH (2ops) (0xAa744AB24B2381240327765D0198a19287B6003A on 44'/60'/6'/0/0) #6 js:2:ethereum_sepolia:0xAa744AB24B2381240327765D0198a19287B6003A:
undefined: 0 𝚝ETH (3ops) (0xC16D418BD9eA367847CCfbC696666c96BEa02781 on 44'/60'/7'/0/0) #7 js:2:ethereum_sepolia:0xC16D418BD9eA367847CCfbC696666c96BEa02781:
undefined: 0 𝚝ETH (3ops) (0x3FD46F0F514302dE5C1314257440d49dfaD25D39 on 44'/60'/8'/0/0) #8 js:2:ethereum_sepolia:0x3FD46F0F514302dE5C1314257440d49dfaD25D39:
undefined: 0 𝚝ETH (0ops) (0xf1d93dB3e9D80478d81eE04924984AeA18808E72 on 44'/60'/9'/0/0) #9 js:2:ethereum_sepolia:0xf1d93dB3e9D80478d81eE04924984AeA18808E72:
undefined: 0 𝚝ETH (0ops) (0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D on 44'/60'/0'/0/0) #0 js:2:ethereum_holesky:0x8a6Af0dD602db0A78EaD07cE9e2595815383FD5D:
Performance ⏲ 29s

Time spent for each spec: (total across mutations)

Spec (accounts) preload scan re-sync tx status sign op broadcast test destination test
TOTAL 426ms 26.2s 8ms N/A N/A N/A N/A N/A
Ethereum (0) 178ms 5.7s N/A N/A N/A N/A N/A N/A
Ethereum Sepolia (9) 121ms 19.1s 8ms N/A N/A N/A N/A N/A
Ethereum Holesky (0) 127ms 1431ms N/A N/A N/A N/A N/A N/A

What is the bot and how does it work? Everything is documented here!

Please sign in to comment.