diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 000000000..76bb9a74a --- /dev/null +++ b/packages/client/README.md @@ -0,0 +1,112 @@ +# `@penumbra-zone/client` + +This package contains interfaces, types, and some helpers for using the page API to Penumbra providers. + +**To use this package, you need to [enable the Buf Schema Registry](https://buf.build/docs/bsr/generated-sdks/npm):** + +```sh +echo "@buf:registry=https://buf.build/gen/npm/v1/" >> .npmrc +``` + +## A simple example + +```ts +import { bech32mAddress } from '@penumbra-zone/bech32m'; +import { createPenumbraClient } from '@penumbra-zone/client/create'; +import { ViewService, SctService } from '@penumbra-zone/protobuf'; + +// This may connect to any available injected provider. +const viewClient = createPenumbraClient(ViewService); + +// Or, you might prefer a specific provider. +const praxViewClient = createPenumbraClient( + ViewService, + 'chrome-extension://lkpmkhpnhknhmibgnmmhdhgdilepfghe', +); + +const { address } = await praxViewClient.addressByIndex({}); +console.log(bech32mAddress(address)); +``` + +## React use + +It's likely you want to use this client in your webapp, and there's a good +chance you're using React or another tanstacklike. Penumbra providers use +`@connectrpc` tooling, so these clients are supported by `@connectrpc/query` and +`@tanstack/react-query`. + +After using `createPenumbraChannelTransport` from `@penumbra-zone/client/create` +and `TransportProvider` from `@connectrpc/query` in a parent component, you can +use convenient React idioms. + +You can see a full example of this at https://github.com/penumbra-zone/nextjs-penumbra-client-example + +### A parent component + +```ts +"use client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { syncCreatePenumbraChannelTransport } from "@penumbra-zone/client/create"; +import { TransportProvider } from "@connectrpc/connect-query"; +import { useMemo } from "react"; + +const queryClient = new QueryClient(); + +export const PenumbraQueryProvider = ({ + providerOrigin, + children, +}: { + providerOrigin: string; + children: React.ReactNode; +}) => { + const penumbraTransport = useMemo( + () => syncCreatePenumbraChannelTransport(providerOrigin), + [providerOrigin], + ); + return ( + + {children} + + ); +}; +``` + +### A querying component + +```ts +"use client"; +import { addressByIndex } from "@buf/penumbra-zone_penumbra.connectrpc_query-es/penumbra/view/v1/view-ViewService_connectquery"; +import { bech32mAddress } from "@penumbra-zone/bech32m/penumbra"; +import { useQuery } from "@connectrpc/connect-query"; + +export const PenumbraAddress = ({ account }: { account?: number }) => { + const { data } = useQuery(addressByIndex, { addressIndex: { account } }); + return ( + data?.address && ( + {bech32mAddress(data.address)} + ) + ); +}; +``` + +## You could access the providers directly, without importing this package. + +This example is javascript. + +```js +import { createChannelTransport } from '@penumbra-zone/transport-dom'; +import { createPromiseClient } from '@connectrpc/connect'; +import { jsonOptions, ViewService } from '@penumbra-zone/protobuf'; + +// naively get first available provider +const provider = Object.values(window[Symbol.for('penumbra')])[0]; +void provider.request(); + +// create a client +const viewClient = createPromiseClient( + ViewService, + createChannelTransport({ jsonOptions, getPort: provider.connect }), +); + +const { catchingUp, fullSyncHeight } = viewClient.status({}); +``` diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index e321a4098..1c84988e6 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -15,7 +15,7 @@ export const PenumbraSymbol = Symbol.for('penumbra'); * fetching the provider manifest. Provider details such as name, version, * website, brief descriptive text, and icons should be available in the * manifest. - * + * * Presently clients may expect the manifest is a chrome extension manifest v3. * @see https://developer.chrome.com/docs/extensions/reference/manifest * @@ -28,23 +28,8 @@ export const PenumbraSymbol = Symbol.for('penumbra'); * provider. It is convenient to provide the `connect` method as the `getPort` * option for `createChannelTransport` from `@penumbra-zone/transport-dom`, or * use the helpers available in `@penumbra-zone/client/create`. - * - ```js - import { jsonOptions } from '@penumbra-zone/protobuf'; - import { createChannelTransport } from '@penumbra-zone/transport-dom'; - - // naively get first available provider - const provider = Object.values(window[Symbol.for('penumbra')])[0]; - void provider.request(); - - // establish a transport - const transport = createChannelTransport({ jsonOptions, getPort: provider.connect }); - - // export function to create client - export const createPenumbraClient = serviceType => createPromiseClient(serviceType, transport); - ``` -* -*/ + * + */ export interface PenumbraInjection { /** Call when creating a channel transport to this provider. Returns a promise * that may resolve with an active `MessagePort`. */