Skip to content

Commit

Permalink
feat: bootstrap wallet-api custom ACRE module and handlers [LIVE-13948]
Browse files Browse the repository at this point in the history
  • Loading branch information
Justkant committed Sep 27, 2024
1 parent 6464ba7 commit 9651e07
Show file tree
Hide file tree
Showing 26 changed files with 916 additions and 78 deletions.
8 changes: 8 additions & 0 deletions .changeset/flat-comics-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"ledger-live-desktop": minor
"live-mobile": minor
"@ledgerhq/live-common": minor
"@ledgerhq/wallet-api-acre-module": minor
---

feat: bootstrap wallet-api custom ACRE module and handlers
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ apps/ledger-live-mobile/src/screens/PTX/ @ledgerhq/p
apps/ledger-live-mobile/src/screens/Swap/ @ledgerhq/ptx
libs/ledger-live-common/src/exchange/ @ledgerhq/ptx
libs/exchange-module/ @ledgerhq/ptx
libs/wallet-api-acre-module/ @ledgerhq/ptx
libs/ledgerjs/packages/hw-app-exchange/ @ledgerhq/ptx
# Wallet API team
**/PlatformAppProviderWrapper.tsx @ledgerhq/wallet-api
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@ import {
ExchangeType,
} from "@ledgerhq/live-common/wallet-api/Exchange/server";
import trackingWrapper from "@ledgerhq/live-common/wallet-api/Exchange/tracking";
import { Operation } from "@ledgerhq/types-live";
import { AccountLike, Operation } from "@ledgerhq/types-live";
import { track } from "~/renderer/analytics/segment";
import { flattenAccountsSelector } from "~/renderer/reducers/accounts";
import { currentRouteNameRef } from "~/renderer/analytics/screenRefs";
import { closePlatformAppDrawer, openExchangeDrawer } from "~/renderer/actions/UI";
import { WebviewProps } from "../Web3AppWebview/types";
import { context } from "~/renderer/drawers/Provider";
import WebviewErrorDrawer from "~/renderer/screens/exchange/Swap2/Form/WebviewErrorDrawer";
import { platformAppDrawerStateSelector } from "~/renderer/reducers/UI";

export function usePTXCustomHandlers(manifest: WebviewProps["manifest"]) {
export function usePTXCustomHandlers(manifest: WebviewProps["manifest"], accounts: AccountLike[]) {
const dispatch = useDispatch();
const accounts = useSelector(flattenAccountsSelector);
const { setDrawer } = React.useContext(context);

const { isOpen: isDrawerOpen } = useSelector(platformAppDrawerStateSelector);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useRef, useState } from "react";
import styled from "styled-components";
import { useSelector } from "react-redux";
import { flattenAccountsSelector } from "~/renderer/reducers/accounts";
import { Web3AppWebview } from "../Web3AppWebview";
import { TopBar } from "./TopBar";
import Box from "../Box";
Expand All @@ -24,7 +26,8 @@ export default function WebPTXPlayer({ manifest, inputs }: WebviewProps) {
const webviewAPIRef = useRef<WebviewAPI>(null);
const [webviewState, setWebviewState] = useState<WebviewState>(initialWebviewState);

const customHandlers = usePTXCustomHandlers(manifest);
const accounts = useSelector(flattenAccountsSelector);
const customHandlers = usePTXCustomHandlers(manifest, accounts);

return (
<Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useMemo } from "react";
import { ipcRenderer } from "electron";
import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { AccountLike } from "@ledgerhq/types-live";
import { useToasts } from "@ledgerhq/live-common/notifications/ToastProvider/index";
import { addPendingOperation } from "@ledgerhq/live-common/account/index";
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types";
import { handlers as acreHandlers } from "@ledgerhq/live-common/wallet-api/ACRE/server";
import trackingWrapper from "@ledgerhq/live-common/wallet-api/ACRE/tracking";
import { track } from "~/renderer/analytics/segment";
import { openModal } from "~/renderer/actions/modals";
import { setDrawer } from "~/renderer/drawers/Provider";
import { OperationDetails } from "~/renderer/drawers/OperationDetails";
import { currentRouteNameRef } from "~/renderer/analytics/screenRefs";
import { updateAccountWithUpdater } from "~/renderer/actions/accounts";
import { WebviewProps } from "../Web3AppWebview/types";

export function useACRECustomHandlers(manifest: WebviewProps["manifest"], accounts: AccountLike[]) {
const { pushToast } = useToasts();
const { t } = useTranslation();
const dispatch = useDispatch();

const tracking = useMemo(
() =>
trackingWrapper(
(
eventName: string,
properties?: Record<string, unknown> | null,
mandatory?: boolean | null,
) =>
track(
eventName,
{
...properties,
flowInitiatedFrom:
currentRouteNameRef.current === "Platform Catalog"
? "Discover"
: currentRouteNameRef.current,
},
mandatory,
),
),
[],
);

return useMemo<WalletAPICustomHandlers>(() => {
return {
...acreHandlers({
accounts,
tracking,
manifest,
uiHooks: {
"custom.acre.messageSign": ({ account, message, onSuccess, onError, onCancel }) => {
ipcRenderer.send("show-app", {});
dispatch(
openModal("MODAL_SIGN_MESSAGE", {
account,
message,
onConfirmationHandler: onSuccess,
onFailHandler: onError,
onClose: onCancel,
}),
);
},
"custom.acre.transactionSign": ({
account,
parentAccount,
signFlowInfos: { canEditFees, hasFeesProvided, liveTx },
options,
onSuccess,
onError,
}) => {
ipcRenderer.send("show-app", {});
dispatch(
openModal("MODAL_SIGN_TRANSACTION", {
canEditFees,
stepId: canEditFees && !hasFeesProvided ? "amount" : "summary",
transactionData: liveTx,
useApp: options?.hwAppId,
dependencies: options?.dependencies,
account,
parentAccount,
onResult: onSuccess,
onCancel: onError,
manifestId: manifest.id,
manifestName: manifest.name,
}),
);
},
"custom.acre.transactionBroadcast": (
account,
parentAccount,
mainAccount,
optimisticOperation,
) => {
dispatch(
updateAccountWithUpdater(mainAccount.id, account =>
addPendingOperation(account, optimisticOperation),
),
);

pushToast({
id: optimisticOperation.id,
type: "operation",
title: t("platform.flows.broadcast.toast.title"),
text: t("platform.flows.broadcast.toast.text"),
icon: "info",
callback: () => {
tracking.broadcastOperationDetailsClick(manifest);
setDrawer(OperationDetails, {
operationId: optimisticOperation.id,
accountId: account.id,
parentId: parentAccount?.id as string | undefined | null,
});
},
});
},
},
}),
};
}, [accounts, tracking, manifest, dispatch, pushToast, t]);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo, useRef, useState } from "react";
import styled from "styled-components";
import { useSelector } from "react-redux";
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types";
import { CurrentAccountHistDB } from "@ledgerhq/live-common/wallet-api/react";
import { handlers as loggerHandlers } from "@ledgerhq/live-common/wallet-api/CustomLogger/server";
Expand All @@ -10,6 +11,8 @@ import { WebviewAPI, WebviewProps, WebviewState } from "../Web3AppWebview/types"
import { initialWebviewState } from "../Web3AppWebview/helpers";
import { usePTXCustomHandlers } from "../WebPTXPlayer/CustomHandlers";
import { useCurrentAccountHistDB } from "~/renderer/screens/platform/v2/hooks";
import { flattenAccountsSelector } from "~/renderer/reducers/accounts";
import { useACRECustomHandlers } from "./CustomHandlers";

export const Container = styled.div`
display: flex;
Expand Down Expand Up @@ -37,14 +40,17 @@ export default function WebPlatformPlayer({ manifest, inputs, onClose, config, .
const webviewAPIRef = useRef<WebviewAPI>(null);
const [webviewState, setWebviewState] = useState<WebviewState>(initialWebviewState);

const customPTXHandlers = usePTXCustomHandlers(manifest);
const accounts = useSelector(flattenAccountsSelector);
const customACREHandlers = useACRECustomHandlers(manifest, accounts);
const customPTXHandlers = usePTXCustomHandlers(manifest, accounts);

const customHandlers = useMemo<WalletAPICustomHandlers>(() => {
return {
...loggerHandlers,
...customACREHandlers,
...customPTXHandlers,
};
}, [customPTXHandlers]);
}, [customACREHandlers, customPTXHandlers]);

const onStateChange: WebviewProps["onStateChange"] = state => {
setWebviewState(state);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const SwapWebView = ({
const swapDefaultTrack = useGetSwapTrackingProperties();

const hasSwapState = !!swapState;
const customPTXHandlers = usePTXCustomHandlers(manifest);
const customPTXHandlers = usePTXCustomHandlers(manifest, accounts);

const { fromCurrency, addressFrom, addressTo } = useMemo(() => {
const [, , fromCurrency, addressFrom] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const SwapWebView = ({ manifest, liveAppUnavailable }: SwapWebProps) => {
const { networkStatus } = useNetworkStatus();
const isOffline = networkStatus === NetworkStatus.OFFLINE;

const customPTXHandlers = usePTXCustomHandlers(manifest);
const customPTXHandlers = usePTXCustomHandlers(manifest, accounts);
const customHandlers = useMemo(
() => ({
...loggerHandlers,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo, useState } from "react";
import { useSelector } from "react-redux";
import type { AccountLike } from "@ledgerhq/types-live";
import type { Device } from "@ledgerhq/live-common/hw/actions/types";
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types";
import trackingWrapper from "@ledgerhq/live-common/wallet-api/Exchange/tracking";
Expand All @@ -8,18 +8,16 @@ import {
ExchangeType,
} from "@ledgerhq/live-common/wallet-api/Exchange/server";
import { useNavigation } from "@react-navigation/native";
import { flattenAccountsSelector } from "~/reducers/accounts";
import { StackNavigatorNavigation } from "../RootNavigator/types/helpers";
import { BaseNavigatorStackParamList } from "../RootNavigator/types/BaseNavigator";
import { track } from "~/analytics";
import { NavigatorName, ScreenName } from "~/const";
import { currentRouteNameRef } from "~/analytics/screenRefs";
import { WebviewProps } from "../Web3AppWebview/types";

export function usePTXCustomHandlers(manifest: WebviewProps["manifest"]) {
export function usePTXCustomHandlers(manifest: WebviewProps["manifest"], accounts: AccountLike[]) {
const navigation = useNavigation<StackNavigatorNavigation<BaseNavigatorStackParamList>>();
const [device, setDevice] = useState<Device>();
const accounts = useSelector(flattenAccountsSelector);

const tracking = useMemo(
() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
TouchableOpacity,
} from "react-native";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";

import { Flex, Icon, Text } from "@ledgerhq/native-ui";
import { AppManifest } from "@ledgerhq/live-common/wallet-api/types";
Expand All @@ -22,6 +23,7 @@ import { useNavigation } from "@react-navigation/native";

import { useTheme } from "styled-components/native";

import { flattenAccountsSelector } from "~/reducers/accounts";
import { WebviewAPI, WebviewState } from "../Web3AppWebview/types";
import { Web3AppWebview } from "../Web3AppWebview";
import { RootNavigationComposite, StackNavigatorNavigation } from "../RootNavigator/types/helpers";
Expand Down Expand Up @@ -262,7 +264,8 @@ export const WebPTXPlayer = ({
}
}, [config, disableHeader, isInternalApp, manifest, navigation, onClose, webviewState?.url]);

const customHandlers = usePTXCustomHandlers(manifest);
const accounts = useSelector(flattenAccountsSelector);
const customHandlers = usePTXCustomHandlers(manifest, accounts);

return (
<SafeAreaView style={[styles.root]}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { useMemo } from "react";
import { useNavigation } from "@react-navigation/native";
import { AccountLike, SignedOperation } from "@ledgerhq/types-live";
import type { Transaction } from "@ledgerhq/live-common/generated/types";
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types";
import { handlers as acreHandlers } from "@ledgerhq/live-common/wallet-api/ACRE/server";
import trackingWrapper from "@ledgerhq/live-common/wallet-api/ACRE/tracking";
import { track } from "~/analytics";
import { NavigatorName, ScreenName } from "~/const";
import { currentRouteNameRef } from "~/analytics/screenRefs";
import { StackNavigatorNavigation } from "../RootNavigator/types/helpers";
import { BaseNavigatorStackParamList } from "../RootNavigator/types/BaseNavigator";
import { WebviewProps } from "../Web3AppWebview/types";
import prepareSignTransaction from "../Web3AppWebview/liveSDKLogic";

export function useACRECustomHandlers(manifest: WebviewProps["manifest"], accounts: AccountLike[]) {
const navigation = useNavigation<StackNavigatorNavigation<BaseNavigatorStackParamList>>();

const tracking = useMemo(
() =>
trackingWrapper((eventName: string, properties?: Record<string, unknown> | null) =>
track(eventName, {
...properties,
flowInitiatedFrom:
currentRouteNameRef.current === "Platform Catalog"
? "Discover"
: currentRouteNameRef.current,
}),
),
[],
);

return useMemo<WalletAPICustomHandlers>(() => {
return {
...acreHandlers({
accounts,
tracking,
manifest,
uiHooks: {
"custom.acre.messageSign": ({ account, message, onSuccess, onError, onCancel }) => {
navigation.navigate(NavigatorName.SignMessage, {
screen: ScreenName.SignSummary,
params: {
message,
accountId: account.id,
onConfirmationHandler: onSuccess,
onFailHandler: onError,
},
onClose: onCancel,
});
},
"custom.acre.transactionSign": ({
account,
parentAccount,
signFlowInfos: { liveTx },
options,
onSuccess,
onError,
}) => {
const tx = prepareSignTransaction(account, parentAccount, liveTx);

navigation.navigate(NavigatorName.SignTransaction, {
screen: ScreenName.SignTransactionSummary,
params: {
currentNavigation: ScreenName.SignTransactionSummary,
nextNavigation: ScreenName.SignTransactionSelectDevice,
transaction: tx as Transaction,
accountId: account.id,
parentId: parentAccount ? parentAccount.id : undefined,
appName: options?.hwAppId,
dependencies: options?.dependencies,
onSuccess: ({
signedOperation,
transactionSignError,
}: {
signedOperation: SignedOperation;
transactionSignError: Error;
}) => {
if (transactionSignError) {
onError(transactionSignError);
} else {
onSuccess(signedOperation);

const n =
navigation.getParent<
StackNavigatorNavigation<BaseNavigatorStackParamList>
>() || navigation;
n.pop();
}
},
onError,
},
});
},
},
}),
};
}, [accounts, tracking, manifest, navigation]);
}
Loading

0 comments on commit 9651e07

Please sign in to comment.