Przeglądaj źródła

[react]: add useMultipleAccounts hook with decoder support (#180)

* [react]: add  hook with decoder support

* chore: fix old changeset

* fix: imports and types

---------

Co-authored-by: nickfrosty <75431177+nickfrosty@users.noreply.github.com>
Kirtiraj Thakor 1 miesiąc temu
rodzic
commit
6ddaef9e53

+ 5 - 0
.changeset/thick-paws-sleep.md

@@ -0,0 +1,5 @@
+---
+"@gillsdk/react": minor
+---
+
+adds useMultipleAccounts hook

+ 42 - 0
packages/react/src/__typeset__/multiple-accounts.ts

@@ -0,0 +1,42 @@
+import { Account, Address } from "gill";
+import { getMetadataDecoder, Metadata } from "gill/programs";
+
+import { useMultipleAccounts } from "../hooks/multiple-accounts.js";
+
+// [DESCRIBE] useMultipleAccounts
+{
+  const addresses = [null] as unknown as [Address<"12345">];
+
+  // Should use default account data type
+  {
+    const { accounts } = useMultipleAccounts({ addresses });
+
+    // Should be a Uint8Array for the data
+    accounts satisfies Account<Uint8Array>[];
+
+    // @ts-expect-error - Should not allow no argument
+    useMultipleAccounts();
+
+    // @ts-expect-error - Should not allow empty argument object
+    useMultipleAccounts({});
+  }
+
+  // Should accept `config` input
+  {
+    const { accounts } = useMultipleAccounts({
+      addresses,
+      config: { commitment: "confirmed" },
+    });
+    accounts satisfies Account<Uint8Array>[];
+  }
+
+  // Should use the decoder type
+  {
+    const { accounts } = useMultipleAccounts({
+      addresses,
+      decoder: getMetadataDecoder(),
+    });
+    // Should be a `Metadata` type for the data
+    accounts satisfies Account<Metadata>[];
+  }
+}

+ 1 - 0
packages/react/src/hooks/index.ts

@@ -2,6 +2,7 @@ export * from "./account.js";
 export * from "./balance.js";
 export * from "./client.js";
 export * from "./latest-blockhash.js";
+export * from "./multiple-accounts.js";
 export * from "./program-accounts.js";
 export * from "./recent-prioritization-fees.js";
 export * from "./signature-statuses.js";

+ 69 - 0
packages/react/src/hooks/multiple-accounts.ts

@@ -0,0 +1,69 @@
+"use client";
+
+import { useQuery } from "@tanstack/react-query";
+import type { Account, Address, Decoder, FetchAccountsConfig, Simplify } from "gill";
+import { assertAccountsExist, decodeAccount, fetchEncodedAccounts } from "gill";
+
+import { GILL_HOOK_CLIENT_KEY } from "../const.js";
+import { useSolanaClient } from "./client.js";
+import type { GillUseRpcHook } from "./types.js";
+
+type RpcConfig = Simplify<Omit<FetchAccountsConfig, "abortSignal">>;
+
+type UseMultipleAccountsInput<
+  TConfig extends RpcConfig = RpcConfig,
+  TAddress extends string = string,
+  TDecodedData extends object = Uint8Array,
+> = GillUseRpcHook<TConfig> & {
+  /**
+   * List of addresses to fetch accounts for.
+   */
+  addresses: Address<TAddress>[];
+  /**
+   * Optional decoder to decode the account's `data` buffer.
+   */
+  decoder?: Decoder<TDecodedData>;
+};
+
+type UseMultipleAccountsResponse<TAddress extends string = string, TDecodedData extends object = Uint8Array> = {
+  accounts: Account<TDecodedData, TAddress>[];
+};
+
+/**
+ * Fetch multiple accounts using the Solana RPC method of
+ * [`getMultipleAccounts`](https://solana.com/docs/rpc/http/getmultipleaccounts)
+ *
+ * Optionally provide a {@link Decoder | `decoder`} to automatically decode all Accounts using it.
+ */
+export function useMultipleAccounts<
+  TConfig extends RpcConfig = RpcConfig,
+  TAddress extends string = string,
+  TDecodedData extends object = Uint8Array,
+>({ addresses, decoder, config, options, abortSignal }: UseMultipleAccountsInput<TConfig, TAddress, TDecodedData>) {
+  const { rpc, urlOrMoniker } = useSolanaClient();
+
+  if (abortSignal) {
+    // @ts-expect-error we stripped the `abortSignal` from the type but are now adding it back in
+    config = {
+      ...(config || {}),
+      abortSignal,
+    };
+  }
+
+  const { data, ...rest } = useQuery({
+    networkMode: "offlineFirst",
+    ...options,
+    enabled: addresses.length > 0,
+    queryFn: async () => {
+      const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
+      assertAccountsExist(maybeAccounts);
+      return maybeAccounts.map((acc) => (decoder ? decodeAccount(acc, decoder) : acc));
+    },
+    queryKey: [GILL_HOOK_CLIENT_KEY, urlOrMoniker, "getMultipleAccounts", addresses, config?.commitment],
+  });
+
+  return {
+    ...rest,
+    accounts: data,
+  } as UseMultipleAccountsResponse<TAddress, TDecodedData>;
+}