Usage of the walletAPIServer in Ledger Wallet
The react hook useWalletAPIServer is used within Ledger Wallet to create a walletAPIServer
It is used in ledger-live-common here
ledger-live-common/src/wallet-api/react.ts
In this file, a walletAPIServer
is created, and exposed through another hook (also called
useWalletAPIServer)
This (LLC hook) is in turn used in both LLD and LLM
useWebview()
useWebView() gets called
Both LLD and LLM, when creating live apps, use the hook useWebView,
with the main arguments being:
- manifest
- customHandlers
- webviewRef
const WalletAPIWebview => {
({ manifest customHandlers} ) => {
const { webviewRef } = useWebviewState();
useWebView({ manifest, customHandlers}, webviewRef);
return <webview />
}
};Prepares transport
Will allow communication with the webview.
const webviewHook = {
return {
postMessage: (message) => {
webviewRef.current.contentWindow?.postMessage(message);
},
};
};Prepares uiHooks
They will allow walletHandlers to trigger ui actions
more on uiHooks
uiHooks are created in useWebView and sent to LLC useWalletAPIServer
uiHook is an object that maps a method like "account.request" to an action in LLD / LLM
here’s a list of actions it triggers in LLD:
- opening a drawer to select an account, start an exchange process
- opening modals to sign transactions, messages, start an exchange process, connect a device
- store data / update account data
- display toaster
LLC useWalletAPIServer() gets called
The goal of LLC’s useWalletAPIServer is simply to call the walletAPIServer hook (also called useWalletAPIServer)
Before doing so it:
-
Extracts permissions From the manifest
-
Converts accounts sent from useWebView (coming from redux store) to a format readable by walletAPIServer
-
Fetches and filters currencies (coming from
libs/ledger-live-common/src/currencies/helpers.tslistCurrencies) -
Converts filtered currencies to a format readable by wallet-api-server. More on that format here
-
Creates a transport
function useTransport(postMessage: (message: string) => void | undefined): Transport {
return useMemo(() => {
return {
onMessage: undefined,
send: postMessage, // will allow WALLETAPISERVER -> LIVEAPP communication (via postMessage)
};
}, [postMessage]);
}
const transport = useTransport(webviewHook.postMessage);walletAPIServer gets instantiated
via the react hook useWalletAPIServer
post-instantiation transport setup
walletAPIServer sends back its onMessage callback, the webview will use
it to send message to it.
const { onMessage } = useWalletAPIServer({
manifest,
accounts,
config,
webviewHook,
uiHook,
customHandlers,
});
const handleMessage = useCallback(
(event: Electron.IpcMessageEvent) => {
if (event.channel === "webviewToParent") {
onMessage(event.args[0]);
}
},
[onMessage]
);
useEffect(() => {
webviewRef.current.addEventListener("ipc-message", handleMessage);
}, [handleMessage, onLoad]);post-instantiation setup of wallet handlers
server.setHandler is called to setup Ledger Wallet Wallet/UI/Store callbacks.
Simplified example of setHandler() call
server.setHandler("account.request", async ({ accounts$, currencies$ }) => {
const currencies = await firstValueFrom(currencies$);
return new Promise((resolve, reject) => {
let currencyList = currencyList = allCurrenciesAndTokens.filter(({ id }) => currencyIds.includes(id));
uiAccountRequest({
accounts$,
currencies: currencyList,
onSuccess: (account: AccountLike, parentAccount: Account | undefined) => {
resolve(accountToWalletAPIAccount(account, parentAccount));
},
onCancel: () => {
reject(new Error("Canceled by user"));
},
});
});
});
}, [manifest, server, tracking, uiAccountRequest]);those handlers are saved in the property WalletAPIServer.walletHandlers
To recap:
WalletAPIServer.walletHandlers-> callbacks defined in LLC, trigger modals / drawers / update redux store, etc.WalletAPIServer.requestHandlers-> internalHandlers + customHandlers, can callwalletHandlersinside of them