UNPKG

@starknet-react/core

Version:

Starknet React is a collection of React hooks for Starknet.

160 lines (142 loc) 4.01 kB
import type { Events } from "@starknet-io/types-js"; import type { Address } from "@starknet-react/chains"; import { type BlockIdentifier as BlockIdentifier_, BlockTag, hash, num, type RpcProvider, } from "starknet"; import { type UseInfiniteQueryProps, type UseInfiniteQueryResult, useInfiniteQuery, } from "../query"; import { useProvider } from "./use-provider"; const DEFAULT_PAGE_SIZE = 5; type EventsType = Events; type BlockIdentifier = Exclude<BlockIdentifier_, bigint>; /** Arguments for `useEvents`. */ export type UseEventsProps = UseInfiniteQueryProps< EventsType, Error, EventsType, EventsType, ReturnType<typeof queryKey>, string > & { /** Filter events emitted by a specific contract address */ address?: Address; // TODO: support complex/nested events, maybe add a `keys` parameter ? /** Filter events using the event name, example: Transfer */ eventName?: string; /** Start fetching events from this block */ fromBlock?: BlockIdentifier; /** Stop fetching events at this block */ toBlock?: BlockIdentifier; /** The number of events returned from each individual query */ pageSize?: number; }; /** Value returned from `useEvents`. */ export type UseEventsResult = Omit< UseInfiniteQueryResult<EventsType, string, Error>, "fetchPreviousPage" | "isFetchingPreviousPage" | "hasPreviousPage" >; /** * Hook to fetch events continuously * * The parameters could be used to filter the events by contract address, name * or specify a range of blocks to get the events from * * The returned object contain different functions and props to fetch next pages */ export function useEvents({ address, eventName, fromBlock: fromBlock_, toBlock: toBlock_, pageSize, }: UseEventsProps): UseEventsResult { const { provider } = useProvider(); const rpcProvider = provider as RpcProvider; const keyFilter = eventName ? [num.toHex(hash.starknetKeccak(eventName))] : []; const keys = [keyFilter]; const fromBlock = fromBlock_ ? blockIdentifierToBlockId(fromBlock_) : undefined; const toBlock = toBlock_ ? blockIdentifierToBlockId(toBlock_) : undefined; const chunkSize = pageSize ? pageSize : DEFAULT_PAGE_SIZE; const fetchEvents = async ({ pageParam, }: { pageParam?: string; }): Promise<EventsType> => { const res = await rpcProvider.getEvents({ from_block: fromBlock, to_block: toBlock, address: address, keys: keys, chunk_size: chunkSize, continuation_token: pageParam === "0" ? undefined : pageParam, }); return res; }; return useInfiniteQuery({ // TODO: useMemo ? queryKey: queryKey({ address, eventName, fromBlock: fromBlock_, toBlock: toBlock_, pageSize, }), queryFn: fetchEvents, initialPageParam: "0", getNextPageParam: (lastPage, _pages) => lastPage.continuation_token, }); } function queryKey({ address, eventName, fromBlock, toBlock, pageSize, }: { address?: Address; eventName?: string; fromBlock?: BlockIdentifier; toBlock?: BlockIdentifier; pageSize?: number; }) { return [ { entity: "events", address, eventName, fromBlock, toBlock, pageSize, }, ] as const; } // Function to transform a BlockIdentifier into a BLOCK_ID function blockIdentifierToBlockId(blockIdentifier: BlockIdentifier) { if (blockIdentifier === null) { return BlockTag.PRE_CONFIRMED; // null maps to 'pre_confirmed' in v8 } if (typeof blockIdentifier === "number") { return { block_number: blockIdentifier }; } if (typeof blockIdentifier === "string") { if (blockIdentifier === "latest" || blockIdentifier === "pending") { return blockIdentifier as BlockTag; // BlockTag values map directly } // If it's a regular string, assume it's a block hash return { block_hash: blockIdentifier }; } throw new Error( `Unsupported BlockIdentifier type: ${typeof blockIdentifier}`, ); }