diff --git a/packages/cli/package.json b/packages/cli/package.json index 6c6cd58ee9..30e4a107e5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -49,6 +49,7 @@ "@latticexyz/protocol-parser": "workspace:*", "@latticexyz/schema-type": "workspace:*", "@latticexyz/store": "workspace:*", + "@latticexyz/store-sync": "workspace:*", "@latticexyz/utils": "workspace:*", "@latticexyz/world": "workspace:*", "@latticexyz/world-module-metadata": "workspace:*", diff --git a/packages/cli/src/deploy/ensureResourceTags.ts b/packages/cli/src/deploy/ensureResourceTags.ts index 497a337a56..86cc4bc6ac 100644 --- a/packages/cli/src/deploy/ensureResourceTags.ts +++ b/packages/cli/src/deploy/ensureResourceTags.ts @@ -11,9 +11,8 @@ import { getContractArtifact } from "../utils/getContractArtifact"; import { createPrepareDeploy } from "./createPrepareDeploy"; import { waitForTransactions } from "./waitForTransactions"; import { LibraryMap } from "./getLibraryMap"; -import { fetchBlockLogs } from "@latticexyz/block-logs-stream"; -import { getStoreLogs, flattenStoreLogs, logToRecord } from "@latticexyz/store/internal"; import { getKeyTuple, getSchemaPrimitives } from "@latticexyz/protocol-parser/internal"; +import { getRecords } from "@latticexyz/store-sync"; const metadataModuleArtifact = getContractArtifact(metadataModule); @@ -40,23 +39,15 @@ export async function ensureResourceTags({ ? { readonly valueToHex?: (value: value) => Hex } : { readonly valueToHex: (value: value) => Hex })): Promise { debug("ensuring", tags.length, "resource tags"); - debug("looking up existing resource tags"); - const blockLogs = await fetchBlockLogs({ - fromBlock: worldDeploy.deployBlock, - toBlock: worldDeploy.stateBlock, - maxBlockRange: 100_000n, - async getLogs({ fromBlock, toBlock }) { - return getStoreLogs(client, { - address: worldDeploy.address, - fromBlock, - toBlock, - tableId: metadataConfig.tables.metadata__ResourceTag.tableId, - }); - }, + + const { records } = await getRecords({ + table: metadataConfig.tables.metadata__ResourceTag, + worldAddress: worldDeploy.address, + indexerUrl: "https://indexer.mud.garnetchain.com", + chainId: 17069, }); - const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); - const records = logs.map((log) => logToRecord({ log, table: metadataConfig.tables.metadata__ResourceTag })); + debug("found", records.length, "resource tags"); const existingTags = new Map( diff --git a/packages/cli/src/deploy/getResourceAccess.ts b/packages/cli/src/deploy/getResourceAccess.ts index 61ecd4f688..356e82c061 100644 --- a/packages/cli/src/deploy/getResourceAccess.ts +++ b/packages/cli/src/deploy/getResourceAccess.ts @@ -1,14 +1,10 @@ import { Client, Hex, Address, getAddress } from "viem"; import { WorldDeploy } from "./common"; import { debug } from "./debug"; -import { decodeKey, getKeySchema, getSchemaTypes } from "@latticexyz/protocol-parser/internal"; -import { getTableValue } from "./getTableValue"; import worldConfig from "@latticexyz/world/mud.config"; -import { fetchBlockLogs } from "@latticexyz/block-logs-stream"; -import { flattenStoreLogs, getStoreLogs } from "@latticexyz/store/internal"; +import { getRecords } from "@latticexyz/store-sync"; export async function getResourceAccess({ - client, worldDeploy, }: { readonly client: Client; @@ -16,45 +12,17 @@ export async function getResourceAccess({ }): Promise { debug("looking up resource access for", worldDeploy.address); - const blockLogs = await fetchBlockLogs({ - fromBlock: worldDeploy.deployBlock, - toBlock: worldDeploy.stateBlock, - maxBlockRange: 100_000n, - async getLogs({ fromBlock, toBlock }) { - return getStoreLogs(client, { - address: worldDeploy.address, - fromBlock, - toBlock, - tableId: worldConfig.namespaces.world.tables.ResourceAccess.tableId, - }); - }, + const { records } = await getRecords({ + table: worldConfig.namespaces.world.tables.ResourceAccess, + worldAddress: worldDeploy.address, + indexerUrl: "https://indexer.mud.garnetchain.com", + chainId: 17069, }); - const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); - - const keys = logs.map((log) => - decodeKey(getSchemaTypes(getKeySchema(worldConfig.namespaces.world.tables.ResourceAccess)), log.args.keyTuple), - ); - - const access = ( - await Promise.all( - keys.map( - async (key) => - [ - key, - await getTableValue({ - client, - worldDeploy, - table: worldConfig.namespaces.world.tables.ResourceAccess, - key, - }), - ] as const, - ), - ) - ) - .filter(([, value]) => value.access) - .map(([key]) => ({ - resourceId: key.resourceId, - address: getAddress(key.caller), + const access = records + .filter((record) => record.access) + .map((record) => ({ + resourceId: record.resourceId, + address: getAddress(record.caller), })); debug("found", access.length, "resource<>address access pairs"); diff --git a/packages/cli/src/deploy/getResourceIds.ts b/packages/cli/src/deploy/getResourceIds.ts index d0714c4a2d..fa3f87f3f2 100644 --- a/packages/cli/src/deploy/getResourceIds.ts +++ b/packages/cli/src/deploy/getResourceIds.ts @@ -1,36 +1,24 @@ import { Client, Hex } from "viem"; -import { flattenStoreLogs, getStoreLogs } from "@latticexyz/store/internal"; import { WorldDeploy } from "./common"; import { debug } from "./debug"; import storeConfig from "@latticexyz/store/mud.config"; -import { fetchBlockLogs } from "@latticexyz/block-logs-stream"; +import { getRecords } from "@latticexyz/store-sync"; export async function getResourceIds({ - client, worldDeploy, }: { readonly client: Client; readonly worldDeploy: WorldDeploy; }): Promise { debug("looking up resource IDs for", worldDeploy.address); - - const blockLogs = await fetchBlockLogs({ - fromBlock: worldDeploy.deployBlock, - toBlock: worldDeploy.stateBlock, - maxBlockRange: 100_000n, - async getLogs({ fromBlock, toBlock }) { - return getStoreLogs(client, { - address: worldDeploy.address, - fromBlock, - toBlock, - tableId: storeConfig.namespaces.store.tables.ResourceIds.tableId, - }); - }, + const { records } = await getRecords({ + table: storeConfig.namespaces.store.tables.ResourceIds, + worldAddress: worldDeploy.address, + indexerUrl: "https://indexer.mud.garnetchain.com", + chainId: 17069, }); - const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); + const resourceIds = records.map((record) => record.resourceId); - const resourceIds = logs.map((log) => log.args.keyTuple[0]); debug("found", resourceIds.length, "resource IDs for", worldDeploy.address); - return resourceIds; } diff --git a/packages/cli/src/deploy/getTables.ts b/packages/cli/src/deploy/getTables.ts index 54b0b717cf..bf9d7b427d 100644 --- a/packages/cli/src/deploy/getTables.ts +++ b/packages/cli/src/deploy/getTables.ts @@ -2,18 +2,10 @@ import { Client, Hex, decodeAbiParameters, parseAbiParameters } from "viem"; import { hexToResource } from "@latticexyz/common"; import { WorldDeploy } from "./common"; import { debug } from "./debug"; -import { - decodeKey, - decodeValueArgs, - getKeySchema, - getSchemaTypes, - getValueSchema, - hexToSchema, -} from "@latticexyz/protocol-parser/internal"; +import { hexToSchema } from "@latticexyz/protocol-parser/internal"; import { Schema, Table } from "@latticexyz/config"; import storeConfig from "@latticexyz/store/mud.config"; -import { fetchBlockLogs } from "@latticexyz/block-logs-stream"; -import { flattenStoreLogs, getStoreLogs } from "@latticexyz/store/internal"; +import { getRecords } from "@latticexyz/store-sync"; // TODO: add label and namespaceLabel once we register it onchain type DeployedTable = Omit & { @@ -24,7 +16,6 @@ type DeployedTable = Omit & { }; export async function getTables({ - client, worldDeploy, }: { readonly client: Client; @@ -32,39 +23,21 @@ export async function getTables({ }): Promise[]> { debug("looking up tables for", worldDeploy.address); - const blockLogs = await fetchBlockLogs({ - fromBlock: worldDeploy.deployBlock, - toBlock: worldDeploy.stateBlock, - maxBlockRange: 100_000n, - async getLogs({ fromBlock, toBlock }) { - return getStoreLogs(client, { - address: worldDeploy.address, - fromBlock, - toBlock, - tableId: storeConfig.namespaces.store.tables.Tables.tableId, - }); - }, + const { records } = await getRecords({ + table: storeConfig.namespaces.store.tables.Tables, + worldAddress: worldDeploy.address, + indexerUrl: "https://indexer.mud.garnetchain.com", + chainId: 17069, }); - const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); // TODO: combine with store-sync logToTable and export from somewhere - const tables = logs.map((log): DeployedTable => { - const { tableId } = decodeKey( - getSchemaTypes(getKeySchema(storeConfig.namespaces.store.tables.Tables)), - log.args.keyTuple, - ); - const { type, namespace, name } = hexToResource(tableId); - const recordValue = decodeValueArgs( - getSchemaTypes(getValueSchema(storeConfig.namespaces.store.tables.Tables)), - log.args, - ); + const tables = records.map((record) => { + const { type, namespace, name } = hexToResource(record.tableId); - const keySchemaHex = recordValue.keySchema; - const valueSchemaHex = recordValue.valueSchema; - const solidityKeySchema = hexToSchema(keySchemaHex); - const solidityValueSchema = hexToSchema(valueSchemaHex); - const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), recordValue.abiEncodedKeyNames)[0]; - const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), recordValue.abiEncodedFieldNames)[0]; + const solidityKeySchema = hexToSchema(record.keySchema); + const solidityValueSchema = hexToSchema(record.valueSchema); + const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), record.abiEncodedKeyNames)[0]; + const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), record.abiEncodedFieldNames)[0]; const valueAbiTypes = [...solidityValueSchema.staticFields, ...solidityValueSchema.dynamicFields]; @@ -80,13 +53,13 @@ export async function getTables({ type: type as never, namespace, name, - tableId, + tableId: record.tableId, schema: { ...keySchema, ...valueSchema }, key: Object.keys(keySchema), keySchema, - keySchemaHex, + keySchemaHex: record.keySchema, valueSchema, - valueSchemaHex, + valueSchemaHex: record.valueSchema, }; }); diff --git a/packages/store-sync/src/getRecords.ts b/packages/store-sync/src/getRecords.ts new file mode 100644 index 0000000000..d7a1b1a9b9 --- /dev/null +++ b/packages/store-sync/src/getRecords.ts @@ -0,0 +1,70 @@ +import { fetchBlockLogs } from "@latticexyz/block-logs-stream"; +import { Table } from "@latticexyz/config"; +import { getSchemaPrimitives } from "@latticexyz/protocol-parser/internal"; +import { LogToRecordArgs, flattenStoreLogs, getStoreLogs, logToRecord } from "@latticexyz/store/internal"; +import { Address, createPublicClient, http } from "viem"; +import { debug } from "./debug"; +import { getSnapshot } from "./getSnapshot"; +import { StorageAdapterLog } from "./common"; + +type GetRecordsOptions = { + table: table; + worldAddress: Address; +} & ( + | { + indexerUrl: string; + chainId: number; + } + | { + rpcUrl: string; + fromBlock?: bigint; + toBlock?: bigint; + } +); + +type GetRecordsResult
= { + records: getSchemaPrimitives[]; + blockNumber: bigint; +}; + +export async function getRecords
( + options: GetRecordsOptions
, +): Promise> { + async function getLogs(): Promise { + if ("indexerUrl" in options) { + debug("fetching records for", options.table.label, "via indexer from", options.indexerUrl); + const logs = await getSnapshot({ + chainId: options.chainId, + address: options.worldAddress, + indexerUrl: options.indexerUrl, + filters: [{ tableId: options.table.tableId }], + }); + return logs?.logs ?? []; + } else { + debug("fetching records for", options.table.label, "via RPC from", options.rpcUrl); + const client = createPublicClient({ + transport: http(options.rpcUrl), + }); + const blockLogs = await fetchBlockLogs({ + fromBlock: options.fromBlock ?? 0n, + toBlock: options.toBlock ?? (await client.getBlockNumber()), + maxBlockRange: 100_000n, + async getLogs({ fromBlock, toBlock }) { + return getStoreLogs(client, { + address: options.worldAddress, + fromBlock, + toBlock, + tableId: options.table.tableId, + }); + }, + }); + return flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); + } + } + + const logs = await getLogs(); + const records = logs.map((log) => logToRecord({ log: log as LogToRecordArgs
["log"], table: options.table })); + const blockNumber = logs.length > 0 ? logs[logs.length - 1].blockNumber ?? 0n : 0n; + debug("found", records.length, "records for table", options.table.label, "at block", blockNumber); + return { records, blockNumber }; +} diff --git a/packages/store-sync/src/index.ts b/packages/store-sync/src/index.ts index e452f19a0d..7c68aeb447 100644 --- a/packages/store-sync/src/index.ts +++ b/packages/store-sync/src/index.ts @@ -7,5 +7,6 @@ export * from "./logToTable"; export * from "./tablesWithRecordsToLogs"; export * from "./tableToLog"; export * from "./recordToLog"; +export * from "./getRecords"; export { logToRecord, type LogToRecordArgs } from "@latticexyz/store/internal"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32725b14b0..caa9c7002c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,6 +184,9 @@ importers: '@latticexyz/store': specifier: workspace:* version: link:../store + '@latticexyz/store-sync': + specifier: workspace:* + version: link:../store-sync '@latticexyz/utils': specifier: workspace:* version: link:../utils