Skip to content

Commit

Permalink
docs(hooks): add initial react hooks docs (#561)
Browse files Browse the repository at this point in the history
* docs(hooks): add initial react hooks docs

* refactor: apply suggestions from code review

Co-authored-by: Ajay Vasisht <43521356+avasisht23@users.noreply.github.com>

---------

Co-authored-by: Ajay Vasisht <43521356+avasisht23@users.noreply.github.com>
  • Loading branch information
moldy530 and avasisht23 authored Apr 10, 2024
1 parent ce3e0b8 commit 5726116
Show file tree
Hide file tree
Showing 32 changed files with 794 additions and 9 deletions.
2 changes: 2 additions & 0 deletions packages/accounts/src/light-account/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type LightAccount<
getOwnerAddress: () => Promise<Address>;
};

//#region CreateLightAccountParams
export type CreateLightAccountParams<
TTransport extends Transport = Transport,
TSigner extends SmartAccountSigner = SmartAccountSigner
Expand All @@ -52,6 +53,7 @@ export type CreateLightAccountParams<
initCode?: Hex;
version?: LightAccountVersion;
};
//#endregion CreateLightAccountParams

export async function createLightAccount<
TTransport extends Transport = Transport,
Expand Down
2 changes: 2 additions & 0 deletions packages/accounts/src/msca/account/multiOwnerAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type MultiOwnerModularAccount<
TSigner extends SmartAccountSigner = SmartAccountSigner
> = SmartContractAccountWithSigner<"MultiOwnerModularAccount", TSigner>;

// #region CreateMultiOwnerModularAccountParams
export type CreateMultiOwnerModularAccountParams<
TTransport extends Transport = Transport,
TSigner extends SmartAccountSigner = SmartAccountSigner
Expand All @@ -40,6 +41,7 @@ export type CreateMultiOwnerModularAccountParams<
accountAddress?: Address;
initCode?: Hex;
};
// #endregion CreateMultiOwnerModularAccountParams

export async function createMultiOwnerModularAccount<
TTransport extends Transport = Transport,
Expand Down
2 changes: 2 additions & 0 deletions packages/alchemy/src/client/smartAccountClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { createAlchemySmartAccountClientFromRpcClient } from "./internal/smartAc
import { createAlchemyPublicRpcClient } from "./rpcClient.js";
import type { AlchemyRpcSchema } from "./types.js";

// #region AlchemySmartAccountClientConfig
export type AlchemySmartAccountClientConfig<
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
Expand All @@ -34,6 +35,7 @@ export type AlchemySmartAccountClientConfig<
SmartAccountClientConfig<transport, chain, account, context>,
"customMiddleware" | "feeEstimator" | "gasEstimator" | "signUserOperation"
>;
// #endregion AlchemySmartAccountClientConfig

export type BaseAlchemyActions<
chain extends Chain | undefined = Chain | undefined,
Expand Down
7 changes: 7 additions & 0 deletions packages/alchemy/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ export type AlchemyAccountsConfig = {
clientStore: ClientStore;
};

// #region CreateConfigProps
export type CreateConfigProps = ConnectionConfig & {
chain: Chain;
sessionConfig?: AlchemySignerParams["sessionConfig"];
/** Optional parameter that allows you to specify a different RPC Url
* or connection to be used specifically by the signer.
* This is useful if you have a different backend proxy for the signer
* than for your Bundler or Node RPC calls.
*/
signerConnection?: ConnectionConfig;
} & Omit<
PartialBy<
Expand All @@ -44,3 +50,4 @@ export type CreateConfigProps = ConnectionConfig & {
>,
"connection"
>;
// #endregion CreateConfigProps
7 changes: 6 additions & 1 deletion packages/alchemy/src/react/hooks/useAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ export type UseAccountResult<TAccount extends SupportedAccountTypes> = {
isLoadingAccount: boolean;
};

export type UseAccountProps<TAccount extends SupportedAccountTypes> =
GetAccountParams<TAccount> & {
skipCreate?: boolean;
};

export function useAccount<TAccount extends SupportedAccountTypes>(
params: GetAccountParams<TAccount> & { skipCreate?: boolean }
params: UseAccountProps<TAccount>
): UseAccountResult<TAccount> {
const { config, queryClient } = useAlchemyAccountContext();
const status = useSignerStatus();
Expand Down
11 changes: 9 additions & 2 deletions packages/alchemy/src/react/hooks/useAuthenticate.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
"use client";

import { useMutation } from "@tanstack/react-query";
import { useMutation, type UseMutateFunction } from "@tanstack/react-query";
import { ClientOnlyPropertyError } from "../../config/errors.js";
import type { User } from "../../signer/index.js";
import type { AuthParams } from "../../signer/signer.js";
import { useAlchemyAccountContext } from "../context.js";
import { useSigner } from "./useSigner.js";

export function useAuthenticate() {
export type UseAuthenticateResult = {
authenticate: UseMutateFunction<User, Error, AuthParams, unknown>;
isPending: boolean;
error: Error | null;
};

export function useAuthenticate(): UseAuthenticateResult {
const { queryClient } = useAlchemyAccountContext();
const signer = useSigner();

Expand Down
3 changes: 3 additions & 0 deletions packages/alchemy/src/react/hooks/useBundlerClient.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"use client";

import { useSyncExternalStore } from "react";
import type { ClientWithAlchemyMethods } from "../../client/types.js";
import { watchBundlerClient } from "../../config/actions/watchBundlerClient.js";
import { useAlchemyAccountContext } from "../context.js";

export type UseBundlerClientResult = ClientWithAlchemyMethods;

export const useBundlerClient = () => {
const { config } = useAlchemyAccountContext();

Expand Down
3 changes: 2 additions & 1 deletion packages/alchemy/src/react/hooks/useSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import { useSyncExternalStore } from "react";
import { watchSigner } from "../../config/actions/watchSigner.js";
import type { AlchemySigner } from "../../signer/index.js";
import { useAlchemyAccountContext } from "../context.js";

export const useSigner = () => {
export const useSigner = (): AlchemySigner | null => {
const { config } = useAlchemyAccountContext();

// TODO: figure out how to handle this on the server
Expand Down
5 changes: 4 additions & 1 deletion packages/alchemy/src/react/hooks/useSignerStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import { useSyncExternalStore } from "react";
import { getSignerStatus } from "../../config/actions/getSignerStatus.js";
import { watchSignerStatus } from "../../config/actions/watchSignerStatus.js";
import type { SignerStatus } from "../../config/store/types.js";
import { AlchemySignerStatus } from "../../signer/types.js";
import { useAlchemyAccountContext } from "../context.js";

export type UseSignerStatusResult = SignerStatus;

const serverStatus = {
status: AlchemySignerStatus.INITIALIZING,
isInitializing: true,
Expand All @@ -14,7 +17,7 @@ const serverStatus = {
isDisconnected: false,
};

export const useSignerStatus = () => {
export const useSignerStatus = (): UseSignerStatusResult => {
const { config } = useAlchemyAccountContext();

return useSyncExternalStore(
Expand Down
7 changes: 5 additions & 2 deletions packages/alchemy/src/react/hooks/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
import { useSyncExternalStore } from "react";
import { getUser } from "../../config/actions/getUser.js";
import { watchUser } from "../../config/actions/watchUser.js";
import type { User } from "../../signer/index.js";
import { useAlchemyAccountContext } from "../context.js";

export const useUser = () => {
export type UseUserResult = User | null;

export const useUser = (): UseUserResult => {
const { config } = useAlchemyAccountContext();

return useSyncExternalStore(
watchUser(config),
() => getUser(config),
() => getUser(config) ?? null,
() => null
);
};
6 changes: 6 additions & 0 deletions packages/alchemy/src/react/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ export {
export { NoAlchemyAccountContextError } from "./errors.js";
export type * from "./hooks/useAccount.js";
export { useAccount } from "./hooks/useAccount.js";
export type * from "./hooks/useAuthenticate.js";
export { useAuthenticate } from "./hooks/useAuthenticate.js";
export type * from "./hooks/useBundlerClient.js";
export { useBundlerClient } from "./hooks/useBundlerClient.js";
export type * from "./hooks/useSigner.js";
export { useSigner } from "./hooks/useSigner.js";
export type * from "./hooks/useSignerStatus.js";
export { useSignerStatus } from "./hooks/useSignerStatus.js";
export type * from "./hooks/useSmartAccountClient.js";
export { useSmartAccountClient } from "./hooks/useSmartAccountClient.js";
export type * from "./hooks/useUser.js";
export { useUser } from "./hooks/useUser.js";
2 changes: 2 additions & 0 deletions packages/core/src/client/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const createPublicErc4337ClientSchema = <
);
});

// #region ConnectionConfigSchema
export const ConnectionConfigSchema = z.union([
z.object({
rpcUrl: z.never().optional(),
Expand All @@ -40,6 +41,7 @@ export const ConnectionConfigSchema = z.union([
jwt: z.string(),
}),
]);
// #endregion ConnectionConfigSchema

export const UserOperationFeeOptionsFieldSchema =
BigNumberishRangeSchema.merge(MultiplierSchema).partial();
Expand Down
15 changes: 15 additions & 0 deletions site/.vitepress/sidebar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ export const sidebar: DefaultTheme.Sidebar = [
},
],
},
{
text: "React Hooks",
base: "/react",
items: [
{ text: "Overview", link: "/overview" },
{ text: "createConfig", link: "/createConfig" },
{ text: "useAuthenticate", link: "/useAuthenticate" },
{ text: "useSmartAccountClient", link: "/useSmartAccountClient" },
{ text: "useAccount", link: "/useAccount" },
{ text: "useSigner", link: "/useSigner" },
{ text: "useSignerStatus", link: "/useSignerStatus" },
{ text: "useUser", link: "/useUser" },
{ text: "useBundlerClient", link: "/useBundlerClient" },
],
},
{
text: "Choosing a smart account",
items: [
Expand Down
79 changes: 79 additions & 0 deletions site/react/createConfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
outline: deep
head:
- - meta
- property: og:title
content: createConfig
- - meta
- name: description
content: An overview of the createConfig function
- - meta
- property: og:description
content: An overview of the createConfig function
- - meta
- name: twitter:title
content: createConfig
- - meta
- name: twitter:description
content: An overview of the createConfig function
---

# createConfig

The `createConfig` method is used to create a configuration object that is used to initialize the `AlchemyAccountProvider`. The output of this function contains all of the state that will be used by the various hooks exported by `@alchemy/aa-alchemy/react`.

::: warning
It's not recommended to use the resulting config directly. However, if you are not using `React` it is possible to build your own custom hooks using the state contained in the config object.
:::

## Import

```ts
import { createConfig } from "@alchemy/aa-alchemy/config";
```

## Usage

<<< @/snippets/react/config.ts

## Parameters

```ts
import { type CreateConfigProps } from "@alchemy/aa-alchemy/config";
```

::: details CreateConfigProps
<<< @/../packages/alchemy/src/config/types.ts#CreateConfigProps
:::

::: details ConnectionConfig
<<< @/../packages/core/src/client/schema.ts#ConnectionConfigSchema
:::

## Return Type

```ts
import { type AlchemyAccountsConfig } from "@alchemy/aa-alchemy/config";
```

Returns an object containing the Alchemy Accounts state.

### bundlerClient

`ClientWithAlchemyMethods`
A JSON RPC client used to make requests to Alchemy's Nodes and Bundler.

### signer

`AlchemySigner`
The underlying signer instance used by Embedded Accounts. This property is only available on the client.

### coreStore

`CoreStore`
This store contains all of the state that can be used on either the client or the server.

### clientStore

`ClientStore`
This store contains only the state available on the client.
70 changes: 70 additions & 0 deletions site/react/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
outline: deep
head:
- - meta
- property: og:title
content: React Hooks Overview
- - meta
- name: description
content: An overview of using React Hooks exported by Account Kit
- - meta
- property: og:description
content: An overview of using React Hooks exported by Account Kit
- - meta
- name: twitter:title
content: React Hooks Overview
- - meta
- name: twitter:description
content: An overview of using React Hooks exported by Account Kit
---

# React Hooks Overview

If you are using Alchemy's RPC and Smart Contract Accounts and building a React application, you can use the React Hooks exported by Account Kit to interact with your Smart Contract Accounts. You're not required to use these hooks to leverage all of the power of Account Kit. The hooks are exported from `@alchemy/aa-alchemy` and can be found within the `@alchemy/aa-alchemy/react` namespace.

::: warning
React hooks are still being developed and the interfaces could change in the future!
:::

## Install the package

To use the React Hooks, you need to install the `@alchemy/aa-alchemy` and `@tanstack/react-query` packages. We use [`react-query`](https://tanstack.com/query/latest/docs/framework/react/overview) to manage async data fetching and mutations in our hooks.

::: code-group

```bash[npm]
npm install @alchemy/aa-alchemy @tanstack/react-query
```

```bash[yarn]
yarn add @alchemy/aa-alchemy @tanstack/react-query
```

```bash[pnpm]
pnpm add @alchemy/aa-alchemy @tanstack/react-query
```

:::

## Create a config

In order to get started, you'll first have to define a config object that can be used to create an `AlchemyAccountContext` that will be used by all of the hooks exported by the library.

::: code-group
<<< @/snippets/react/config.ts
:::

## Wrap app in Context Provider

Next, you'll need to add the `AlchemyAccountProvider` to your application and pass in the config object and an instance of the `react-query` `QueryClient`.

::: code-group

<<< @/snippets/react/app.tsx

<<< @/snippets/react/config.ts
:::

## Use the hooks

Explore the remaining hooks docs and use them in your application!
Loading

0 comments on commit 5726116

Please sign in to comment.