Skip to content

Commit

Permalink
Merge pull request #4985 from LedgerHQ/feat/wallet-api-custom-handlers
Browse files Browse the repository at this point in the history
feat(wallet-api): add custom handler support [LIVE-9082]
  • Loading branch information
Justkant authored Oct 17, 2023
2 parents 9d9f8bb + f19e382 commit 64cb103
Show file tree
Hide file tree
Showing 22 changed files with 243 additions and 58 deletions.
7 changes: 7 additions & 0 deletions .changeset/soft-foxes-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"ledger-live-desktop": minor
"live-mobile": minor
"@ledgerhq/live-common": minor
---

feat(wallet-api): add custom handler support
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,10 @@ const useGetUserId = () => {
return userId;
};

function useWebView({ manifest }: Pick<Props, "manifest">, webviewRef: RefObject<WebviewTag>) {
function useWebView(
{ manifest, customHandlers }: Pick<WebviewProps, "manifest" | "customHandlers">,
webviewRef: RefObject<WebviewTag>,
) {
const accounts = useSelector(flattenAccountsSelector);

const uiHook = useUiHook(manifest);
Expand Down Expand Up @@ -234,6 +237,7 @@ function useWebView({ manifest }: Pick<Props, "manifest">, webviewRef: RefObject
config,
webviewHook,
uiHook,
customHandlers,
});

const handleMessage = useCallback(
Expand Down Expand Up @@ -293,13 +297,8 @@ function useWebView({ manifest }: Pick<Props, "manifest">, webviewRef: RefObject
return { webviewRef, widgetLoaded, onReload, webviewStyle };
}

interface Props {
manifest: AppManifest;
inputs?: Record<string, string | undefined>;
}

export const WalletAPIWebview = forwardRef<WebviewAPI, WebviewProps>(
({ manifest, inputs = {}, onStateChange }, ref) => {
({ manifest, inputs = {}, customHandlers, onStateChange }, ref) => {
const { webviewState, webviewRef, webviewProps, handleRefresh } = useWebviewState(
{ manifest, inputs },
ref,
Expand All @@ -313,6 +312,7 @@ export const WalletAPIWebview = forwardRef<WebviewAPI, WebviewProps>(
const { webviewStyle, widgetLoaded } = useWebView(
{
manifest,
customHandlers,
},
webviewRef,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import TrackPage from "~/renderer/analytics/TrackPage";
import { WebviewAPI, WebviewProps } from "./types";

export const Web3AppWebview = forwardRef<WebviewAPI, WebviewProps>(
({ manifest, inputs, onStateChange }, ref) => {
({ manifest, inputs, customHandlers, onStateChange }, ref) => {
<TrackPage category="Platform" name="App" appId={manifest.id} params={inputs} />;

if (semver.satisfies(WALLET_API_VERSION, manifest.apiVersion)) {
return (
<WalletAPIWebview
manifest={manifest}
inputs={inputs}
customHandlers={customHandlers}
onStateChange={onStateChange}
ref={ref}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types";
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types";
import { WebContents } from "electron";

export interface WebviewTag extends Electron.WebviewTag {
Expand All @@ -10,6 +11,7 @@ export type WebviewProps = {
manifest: LiveAppManifest;
inputs?: Record<string, string | boolean | undefined>;
onStateChange?: (webviewState: WebviewState) => void;
customHandlers?: WalletAPICustomHandlers;
};

export type WebviewState = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useRef, useState } from "react";
import React, { useMemo, useRef, useState } from "react";
import styled from "styled-components";
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types";
import { handlers as loggerHandlers } from "@ledgerhq/live-common/wallet-api/CustomLogger/server";
import { Web3AppWebview } from "../Web3AppWebview";
import { TopBar, TopBarConfig } from "./TopBar";
import Box from "../Box";
Expand Down Expand Up @@ -32,6 +34,12 @@ export default function WebPlatformPlayer({ manifest, inputs, onClose, config }:
const webviewAPIRef = useRef<WebviewAPI>(null);
const [webviewState, setWebviewState] = useState<WebviewState>(initialWebviewState);

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

return (
<Container>
<Wrapper>
Expand All @@ -47,6 +55,7 @@ export default function WebPlatformPlayer({ manifest, inputs, onClose, config }:
inputs={inputs}
onStateChange={setWebviewState}
ref={webviewAPIRef}
customHandlers={customHandlers}
/>
</Wrapper>
</Container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,21 @@ import { NetworkError } from "./NetworkError";
import { DEFAULT_MULTIBUY_APP_ID } from "@ledgerhq/live-common/wallet-api/constants";

export const WalletAPIWebview = forwardRef<WebviewAPI, WebviewProps>(
({ manifest, inputs = {}, onStateChange, allowsBackForwardNavigationGestures = true }, ref) => {
(
{
manifest,
inputs = {},
customHandlers,
onStateChange,
allowsBackForwardNavigationGestures = true,
},
ref,
) => {
const { onMessage, onLoadError, webviewProps, webviewRef } = useWebView(
{
manifest,
inputs,
customHandlers,
},
ref,
onStateChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ import * as bridge from "../../../e2e/bridge/client";
import Config from "react-native-config";

export function useWebView(
{ manifest, inputs }: Pick<WebviewProps, "manifest" | "inputs">,
{
manifest,
inputs,
customHandlers,
}: Pick<WebviewProps, "manifest" | "inputs" | "customHandlers">,
ref: React.ForwardedRef<WebviewAPI>,
onStateChange: WebviewProps["onStateChange"],
) {
Expand Down Expand Up @@ -86,6 +90,7 @@ export function useWebView(
config,
webviewHook,
uiHook,
customHandlers,
});

const onMessage = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { PlatformAPIWebview } from "./PlatformAPIWebview";
import { WebviewAPI, WebviewProps } from "./types";

export const Web3AppWebview = forwardRef<WebviewAPI, WebviewProps>(
({ manifest, inputs, onStateChange, allowsBackForwardNavigationGestures }, ref) => {
(
{ manifest, inputs, customHandlers, onStateChange, allowsBackForwardNavigationGestures },
ref,
) => {
if (semver.satisfies(WALLET_API_VERSION, manifest.apiVersion)) {
return (
<WalletAPIWebview
ref={ref}
manifest={manifest}
inputs={inputs}
customHandlers={customHandlers}
onStateChange={onStateChange}
allowsBackForwardNavigationGestures={allowsBackForwardNavigationGestures}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types";
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types";
import WebView from "react-native-webview";

export type WebviewProps = {
manifest: LiveAppManifest;
inputs?: Record<string, string | undefined>;
onStateChange?: (webviewState: WebviewState) => void;
allowsBackForwardNavigationGestures?: boolean;
customHandlers?: WalletAPICustomHandlers;
};

export type WebviewState = {
Expand Down
5 changes: 3 additions & 2 deletions libs/ledger-live-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@
"@ledgerhq/live-network": "workspace:^",
"@ledgerhq/live-promise": "workspace:^",
"@ledgerhq/logs": "workspace:^",
"@ledgerhq/wallet-api-core": "^1.2.0",
"@ledgerhq/wallet-api-server": "^1.2.0",
"@ledgerhq/wallet-api-client": "^1.2.0",
"@ledgerhq/wallet-api-core": "^1.3.0",
"@ledgerhq/wallet-api-server": "^1.3.0",
"@solana/spl-token": "^0.3.7",
"@solana/web3.js": "1.77.3",
"@stacks/network": "1.2.2",
Expand Down
23 changes: 23 additions & 0 deletions libs/ledger-live-common/src/wallet-api/CustomLogger/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { CustomModule } from "@ledgerhq/wallet-api-client";

export class CustomLogger extends CustomModule {
debug(message: string) {
return this.request("custom.logger.debug", { message });
}

error(message: string) {
return this.request("custom.logger.error", { message });
}

info(message: string) {
return this.request("custom.logger.info", { message });
}

log(message: string) {
return this.request("custom.logger.log", { message });
}

warn(message: string) {
return this.request("custom.logger.warn", { message });
}
}
23 changes: 23 additions & 0 deletions libs/ledger-live-common/src/wallet-api/CustomLogger/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable no-console */
import { RPCHandler, customWrapper } from "@ledgerhq/wallet-api-server";
import { LoggerParams, LoggerResponse, MethodIds } from "./types";

type Handlers = Record<MethodIds, RPCHandler<unknown, never>>;

export const handlers = {
"custom.logger.debug": customWrapper<LoggerParams, LoggerResponse>(params =>
console.debug(params?.message),
),
"custom.logger.error": customWrapper<LoggerParams, LoggerResponse>(params =>
console.error(params?.message),
),
"custom.logger.info": customWrapper<LoggerParams, LoggerResponse>(params =>
console.info(params?.message),
),
"custom.logger.log": customWrapper<LoggerParams, LoggerResponse>(params =>
console.log(params?.message),
),
"custom.logger.warn": customWrapper<LoggerParams, LoggerResponse>(params =>
console.warn(params?.message),
),
} as const satisfies Handlers;
15 changes: 15 additions & 0 deletions libs/ledger-live-common/src/wallet-api/CustomLogger/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const methodIds = [
"custom.logger.debug",
"custom.logger.error",
"custom.logger.info",
"custom.logger.log",
"custom.logger.warn",
] as const;

export type MethodIds = (typeof methodIds)[number];

export type LoggerParams = {
message: string;
};

export type LoggerResponse = void;
29 changes: 17 additions & 12 deletions libs/ledger-live-common/src/wallet-api/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
getAccountIdFromWalletAccountId,
} from "./converters";
import { isWalletAPISupportedCurrency } from "./helpers";
import { WalletAPICurrency, AppManifest, WalletAPIAccount } from "./types";
import { WalletAPICurrency, AppManifest, WalletAPIAccount, WalletAPICustomHandlers } from "./types";
import { getMainAccount, getParentAccount } from "../account";
import { listCurrencies, findCryptoCurrencyById, findTokenById } from "../currencies";
import { TrackingAPI } from "./tracking";
Expand Down Expand Up @@ -264,6 +264,19 @@ function useDeviceTransport({ manifest, tracking }) {

const allCurrenciesAndTokens = listCurrencies(true);

export type useWalletAPIServerOptions = {
manifest: AppManifest;
accounts: AccountLike[];
tracking: TrackingAPI;
config: ServerConfig;
webviewHook: {
reload: () => void;
postMessage: (message: string) => void;
};
uiHook: Partial<UiHook>;
customHandlers?: WalletAPICustomHandlers;
};

export function useWalletAPIServer({
manifest,
accounts,
Expand All @@ -283,17 +296,8 @@ export function useWalletAPIServer({
"exchange.start": uiExchangeStart,
"exchange.complete": uiExchangeComplete,
},
}: {
manifest: AppManifest;
accounts: AccountLike[];
tracking: TrackingAPI;
config: ServerConfig;
webviewHook: {
reload: () => void;
postMessage: (message: string) => void;
};
uiHook: Partial<UiHook>;
}): {
customHandlers,
}: useWalletAPIServerOptions): {
onMessage: (event: string) => void;
widgetLoaded: boolean;
onLoad: () => void;
Expand All @@ -313,6 +317,7 @@ export function useWalletAPIServer({
accounts: walletAPIAccounts,
currencies: walletAPICurrencies,
permission,
customHandlers,
});

useEffect(() => {
Expand Down
3 changes: 2 additions & 1 deletion libs/ledger-live-common/src/wallet-api/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { SignedOperation } from "@ledgerhq/types-live";
import type { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
import type { Transaction as WalletAPITransaction } from "@ledgerhq/wallet-api-core";
import type { CustomHandlers as WalletAPICustomHandlers } from "@ledgerhq/wallet-api-server";
import type { Transaction } from "../generated/types";
import { LiveAppManifest } from "../platform/types";

export type { WalletAPITransaction };
export type { WalletAPITransaction, WalletAPICustomHandlers };

export type {
Families as WalletAPIFamilies,
Expand Down
2 changes: 1 addition & 1 deletion libs/test-utils/dummy-live-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "../../../../../tsconfig.base",
"extends": "../../../tsconfig.base",
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],

Expand Down
5 changes: 3 additions & 2 deletions libs/test-utils/dummy-wallet-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"@ledgerhq/hw-app-eth": "workspace:*",
"@ledgerhq/hw-transport": "workspace:*",
"@ledgerhq/live-common": "workspace:*",
"@ledgerhq/wallet-api-client": "^1.1.0",
"@ledgerhq/wallet-api-simulator": "^1.0.5",
"@ledgerhq/wallet-api-client": "^1.2.0",
"@ledgerhq/wallet-api-client-react": "^1.1.1",
"@ledgerhq/wallet-api-simulator": "^1.1.0",
"bignumber.js": "^9.1.2",
"buffer": "^6.0.3",
"react": "^18.2.0",
Expand Down
4 changes: 4 additions & 0 deletions libs/test-utils/dummy-wallet-app/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@
background-color: #b8b8b9;
text-align: start;
}

pre {
text-align: initial;
}
Loading

1 comment on commit 64cb103

@vercel
Copy link

@vercel vercel bot commented on 64cb103 Oct 17, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.