import { getTables } from "@/schema/utils.js"; import type { Prettify } from "@/types/utils.js"; import type { Abi, Account, Chain, Client, ContractFunctionArgs, ContractFunctionName, GetBalanceParameters, GetBalanceReturnType, GetCodeParameters, GetCodeReturnType, GetEnsNameParameters, GetEnsNameReturnType, GetStorageAtParameters, GetStorageAtReturnType, MulticallParameters, MulticallReturnType, PublicRpcSchema, ReadContractParameters, ReadContractReturnType, Transport, } from "viem"; import { getBalance as viemGetBalance, getCode as viemGetCode, getEnsName as viemGetEnsName, getStorageAt as viemGetStorageAt, multicall as viemMulticall, readContract as viemReadContract, } from "viem/actions"; import type { Service, create } from "./service.js"; export type BlockOptions = | { cache?: undefined; blockNumber?: undefined; } | { cache: "immutable"; blockNumber?: undefined; } | { cache?: undefined; blockNumber: bigint; }; export type PonderActions = { getBalance: ( args: Omit & BlockOptions, ) => Promise; getCode: ( args: Omit & BlockOptions, ) => Promise; getStorageAt: ( args: Omit & BlockOptions, ) => Promise; multicall: < const contracts extends readonly unknown[], allowFailure extends boolean = true, >( args: Omit< MulticallParameters, "blockTag" | "blockNumber" > & BlockOptions, ) => Promise>; readContract: < const abi extends Abi | readonly unknown[], functionName extends ContractFunctionName, const args extends ContractFunctionArgs, >( args: Omit< ReadContractParameters, "blockTag" | "blockNumber" > & BlockOptions, ) => Promise>; getEnsName: ( args: Omit & BlockOptions, ) => Promise; }; export type ReadOnlyClient< transport extends Transport = Transport, chain extends Chain | undefined = Chain | undefined, > = Prettify< Client >; export const buildCachedActions = ( contextState: Pick, ) => { return < TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, TAccount extends Account | undefined = Account | undefined, >( client: Client, ): PonderActions => ({ getBalance: ({ cache, blockNumber: userBlockNumber, ...args }: Omit & BlockOptions): Promise => viemGetBalance(client, { ...args, ...(cache === "immutable" ? { blockTag: "latest" } : { blockNumber: userBlockNumber ?? contextState.blockNumber }), }), getCode: ({ cache, blockNumber: userBlockNumber, ...args }: Omit & BlockOptions): Promise => viemGetCode(client, { ...args, ...(cache === "immutable" ? { blockTag: "latest" } : { blockNumber: userBlockNumber ?? contextState.blockNumber }), }), getStorageAt: ({ cache, blockNumber: userBlockNumber, ...args }: Omit & BlockOptions): Promise => viemGetStorageAt(client, { ...args, ...(cache === "immutable" ? { blockTag: "latest" } : { blockNumber: userBlockNumber ?? contextState.blockNumber }), }), multicall: < const contracts extends readonly unknown[], allowFailure extends boolean = true, >({ cache, blockNumber: userBlockNumber, ...args }: Omit< MulticallParameters, "blockTag" | "blockNumber" > & BlockOptions): Promise> => viemMulticall(client, { ...args, ...(cache === "immutable" ? { blockTag: "latest" } : { blockNumber: userBlockNumber ?? contextState.blockNumber }), }), // @ts-ignore readContract: < const abi extends Abi | readonly unknown[], functionName extends ContractFunctionName, const args extends ContractFunctionArgs< abi, "pure" | "view", functionName >, >({ cache, blockNumber: userBlockNumber, ...args }: Omit< ReadContractParameters, "blockTag" | "blockNumber" > & BlockOptions): Promise> => viemReadContract(client, { ...args, ...(cache === "immutable" ? { blockTag: "latest" } : { blockNumber: userBlockNumber ?? contextState.blockNumber }), } as ReadContractParameters), getEnsName: ({ cache, blockNumber: userBlockNumber, ...args }: Omit & BlockOptions): Promise => viemGetEnsName(client, { ...args, ...(cache === "immutable" ? { blockTag: "latest" } : { blockNumber: userBlockNumber ?? contextState.blockNumber }), }), }); }; export const buildDb = ({ common, schema, indexingStore, contextState, }: Pick[0], "common" | "schema" | "indexingStore"> & { contextState: Pick< Service["currentEvent"]["contextState"], "encodedCheckpoint" >; }) => { return Object.keys(getTables(schema)).reduce< Service["currentEvent"]["context"]["db"] >((acc, tableName) => { acc[tableName] = { findUnique: async ({ id }) => { common.logger.trace({ service: "store", msg: `${tableName}.findUnique(id=${id})`, }); return indexingStore.findUnique({ tableName, id, }); }, findMany: async ({ where, orderBy, limit, before, after } = {}) => { common.logger.trace({ service: "store", msg: `${tableName}.findMany`, }); return indexingStore.findMany({ tableName, where, orderBy, limit, before, after, }); }, create: async ({ id, data }) => { common.logger.trace({ service: "store", msg: `${tableName}.create(id=${id})`, }); return indexingStore.create({ tableName, encodedCheckpoint: contextState.encodedCheckpoint, id, data, }); }, createMany: async ({ data }) => { common.logger.trace({ service: "store", msg: `${tableName}.createMany(count=${data.length})`, }); return indexingStore.createMany({ tableName, encodedCheckpoint: contextState.encodedCheckpoint, data, }); }, update: async ({ id, data }) => { common.logger.trace({ service: "store", msg: `${tableName}.update(id=${id})`, }); return indexingStore.update({ tableName, encodedCheckpoint: contextState.encodedCheckpoint, id, data, }); }, updateMany: async ({ where, data }) => { common.logger.trace({ service: "store", msg: `${tableName}.updateMany`, }); return indexingStore.updateMany({ tableName, encodedCheckpoint: contextState.encodedCheckpoint, where, data, }); }, upsert: async ({ id, create, update }) => { common.logger.trace({ service: "store", msg: `${tableName}.upsert(id=${id})`, }); return indexingStore.upsert({ tableName, encodedCheckpoint: contextState.encodedCheckpoint, id, create, update, }); }, delete: async ({ id }) => { common.logger.trace({ service: "store", msg: `${tableName}.delete(id=${id})`, }); return indexingStore.delete({ tableName, encodedCheckpoint: contextState.encodedCheckpoint, id, }); }, }; return acc; }, {}); };