diff --git a/.changeset/early-colts-smile.md b/.changeset/early-colts-smile.md new file mode 100644 index 0000000000..509dc9bf4a --- /dev/null +++ b/.changeset/early-colts-smile.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/block-logs-stream": patch +--- + +`fetchLogs` and `blockRangeToLogs` now accept a `getLogs` option to override the default behavior. diff --git a/.changeset/gold-mangos-sneeze.md b/.changeset/gold-mangos-sneeze.md new file mode 100644 index 0000000000..90c3f30762 --- /dev/null +++ b/.changeset/gold-mangos-sneeze.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": patch +--- + +Added `getStoreLogs` and `flattenStoreLogs` to aid in fetching data from store contracts. For now, these are internal exports and considered unstable/experimental. diff --git a/.changeset/nervous-clouds-collect.md b/.changeset/nervous-clouds-collect.md new file mode 100644 index 0000000000..4a2a776ae0 --- /dev/null +++ b/.changeset/nervous-clouds-collect.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/cli": patch +--- + +Deployer now has a better method for fetching store logs from the world that should be more efficient and resilient to block range errors and rate limiting. diff --git a/.changeset/ten-llamas-protect.md b/.changeset/ten-llamas-protect.md new file mode 100644 index 0000000000..3d1a0de08f --- /dev/null +++ b/.changeset/ten-llamas-protect.md @@ -0,0 +1,12 @@ +--- +"@latticexyz/common": patch +--- + +Added `logSort` method to help when sorting logs fetched from RPC, where they come back ordered relative to the topics used. + +```ts +import { logSort } from "@latticexyz/common"; + +const logs = getLogs(...); +logs.sort(logSort); +``` diff --git a/packages/block-logs-stream/src/blockRangeToLogs.ts b/packages/block-logs-stream/src/blockRangeToLogs.ts index 76084db60a..39c7866fb8 100644 --- a/packages/block-logs-stream/src/blockRangeToLogs.ts +++ b/packages/block-logs-stream/src/blockRangeToLogs.ts @@ -1,29 +1,13 @@ import { EMPTY, OperatorFunction, concatMap, from, pipe, tap } from "rxjs"; -import { FetchLogsResult, fetchLogs } from "./fetchLogs"; +import { FetchLogsOptions, FetchLogsResult, fetchLogs } from "./fetchLogs"; import { AbiEvent } from "abitype"; -import { Address, BlockNumber, Client } from "viem"; +import { BlockNumber, UnionOmit } from "viem"; import { debug } from "./debug"; -export type BlockRangeToLogsOptions = { - /** - * [viem `Client`][0] used for fetching logs from the RPC. - * - * [0]: https://viem.sh/docs/clients/public.html - */ - publicClient: Client; - /** - * Optional contract address(es) to fetch logs for. - */ - address?: Address | Address[]; - /** - * Events to fetch logs for. - */ - events: abiEvents; - /** - * Optional maximum block range, if your RPC limits the amount of blocks fetched at a time. - */ - maxBlockRange?: bigint; -}; +export type BlockRangeToLogsOptions = UnionOmit< + FetchLogsOptions, + "fromBlock" | "toBlock" +>; export type BlockRangeToLogsResult = OperatorFunction< { startBlock: BlockNumber; endBlock: BlockNumber }, @@ -38,12 +22,9 @@ export type BlockRangeToLogsResult = Oper * @param {BlockRangeToLogsOptions} options See `BlockRangeToLogsOptions`. * @returns {BlockRangeToLogsResult} An operator function that transforms a stream of block ranges into a stream of fetched logs. */ -export function blockRangeToLogs({ - publicClient, - address, - events, - maxBlockRange, -}: BlockRangeToLogsOptions): BlockRangeToLogsResult { +export function blockRangeToLogs( + opts: BlockRangeToLogsOptions, +): BlockRangeToLogsResult { let fromBlock: bigint; let toBlock: bigint; @@ -57,16 +38,7 @@ export function blockRangeToLogs({ concatMap(() => { if (fromBlock > toBlock) return EMPTY; debug(`fetching logs for block range ${fromBlock}-${toBlock}`); - return from( - fetchLogs({ - publicClient, - address, - events, - fromBlock, - toBlock, - maxBlockRange, - }), - ).pipe( + return from(fetchLogs({ ...opts, fromBlock, toBlock })).pipe( tap(({ toBlock }) => { fromBlock = toBlock + 1n; }), diff --git a/packages/block-logs-stream/src/fetchLogs.ts b/packages/block-logs-stream/src/fetchLogs.ts index a12a1a3574..f5b4da097d 100644 --- a/packages/block-logs-stream/src/fetchLogs.ts +++ b/packages/block-logs-stream/src/fetchLogs.ts @@ -1,25 +1,11 @@ import { AbiEvent } from "abitype"; -import { Address, Client, BlockNumber, GetLogsReturnType } from "viem"; +import { Address, Client, BlockNumber, GetLogsReturnType, OneOf } from "viem"; import { bigIntMin, wait } from "@latticexyz/common/utils"; import { debug } from "./debug"; import { getAction } from "viem/utils"; -import { getLogs } from "viem/actions"; +import { getLogs as viem_getLogs } from "viem/actions"; export type FetchLogsOptions = { - /** - * [viem `Client`][0] used for fetching logs from the RPC. - * - * [0]: https://viem.sh/docs/clients/public.html - */ - publicClient: Client; - /** - * Optional contract address(es) to fetch logs for. - */ - address?: Address | Address[]; - /** - * Events to fetch logs for. - */ - events: abiEvents; /** * The block number to start fetching logs from (inclusive). */ @@ -36,7 +22,33 @@ export type FetchLogsOptions = { * Optional maximum amount of retries if the RPC returns a rate limit error. Defaults to 3. */ maxRetryCount?: number; -}; +} & OneOf< + | { + /** + * Async function to return logs for the given block range. + */ + getLogs: (args: { + fromBlock: bigint; + toBlock: bigint; + }) => Promise>; + } + | { + /** + * [viem `Client`][0] used for fetching logs from the RPC. + * + * [0]: https://viem.sh/docs/clients/public.html + */ + publicClient: Client; + /** + * Optional contract address(es) to fetch logs for. + */ + address?: Address | Address[]; + /** + * Events to fetch logs for. + */ + events: abiEvents; + } +>; export type FetchLogsResult = { fromBlock: BlockNumber; @@ -96,25 +108,31 @@ const BLOCK_RANGE_ERRORS = [ export async function* fetchLogs({ maxBlockRange = 1000n, maxRetryCount = 3, - publicClient, - ...getLogsOpts + fromBlock: initialFromBlock, + toBlock: initialToBlock, + ...opts }: FetchLogsOptions): AsyncGenerator> { - let fromBlock = getLogsOpts.fromBlock; - let blockRange = bigIntMin(maxBlockRange, getLogsOpts.toBlock - fromBlock); + const getLogs = + opts.getLogs ?? + (async (blockRange): Promise> => + getAction( + opts.publicClient, + viem_getLogs, + "getLogs", + )({ ...blockRange, address: opts.address, events: opts.events, strict: true })); + + let fromBlock = initialFromBlock; + let blockRange = bigIntMin(maxBlockRange, initialToBlock - fromBlock); let retryCount = 0; - while (fromBlock <= getLogsOpts.toBlock) { + while (fromBlock <= initialToBlock) { try { const toBlock = fromBlock + blockRange; debug(`getting logs for blocks ${fromBlock}-${toBlock} (${blockRange} blocks, ${maxBlockRange} max)`); - const logs = await getAction( - publicClient, - getLogs, - "getLogs", - )({ ...getLogsOpts, fromBlock, toBlock, strict: true }); + const logs = await getLogs({ fromBlock, toBlock }); yield { fromBlock, toBlock, logs }; fromBlock = toBlock + 1n; - blockRange = bigIntMin(maxBlockRange, getLogsOpts.toBlock - fromBlock); + blockRange = bigIntMin(maxBlockRange, initialToBlock - fromBlock); retryCount = 0; } catch (error: unknown) { if (!(error instanceof Error)) throw error; diff --git a/packages/block-logs-stream/src/groupLogsByBlockNumber.ts b/packages/block-logs-stream/src/groupLogsByBlockNumber.ts index 82f6b8ba6e..1afcafff48 100644 --- a/packages/block-logs-stream/src/groupLogsByBlockNumber.ts +++ b/packages/block-logs-stream/src/groupLogsByBlockNumber.ts @@ -1,5 +1,6 @@ import { BlockNumber } from "viem"; -import { bigIntSort, isDefined } from "@latticexyz/common/utils"; +import { logSort } from "@latticexyz/common"; +import { bigIntSort, groupBy } from "@latticexyz/common/utils"; type PartialLog = { readonly blockNumber: bigint; readonly logIndex: number }; @@ -29,22 +30,12 @@ export function groupLogsByBlockNumber( const blockNumbers = Array.from(new Set(logs.map((log) => log.blockNumber))); blockNumbers.sort(bigIntSort); - const groupedBlocks = blockNumbers - .map((blockNumber) => { - const blockLogs = logs.filter((log) => log.blockNumber === blockNumber); - if (!blockLogs.length) return; - blockLogs.sort((a, b) => (a.logIndex < b.logIndex ? -1 : a.logIndex > b.logIndex ? 1 : 0)); + const sortedLogs = logs.slice().sort(logSort); + const groupedBlocks = Array.from(groupBy(sortedLogs, (log) => log.blockNumber).entries()) + .map(([blockNumber, logs]) => ({ blockNumber, logs })) + .filter((block) => block.logs.length > 0); - if (!blockLogs.length) return; - - return { - blockNumber, - logs: blockLogs, - }; - }) - .filter(isDefined); - - const lastBlockNumber = blockNumbers.length > 0 ? blockNumbers[blockNumbers.length - 1] : null; + const lastBlockNumber = blockNumbers.at(-1); if (toBlock != null && (lastBlockNumber == null || toBlock > lastBlockNumber)) { groupedBlocks.push({ diff --git a/packages/cli/src/deploy/getResourceAccess.ts b/packages/cli/src/deploy/getResourceAccess.ts index 825ee4351c..6a793979bc 100644 --- a/packages/cli/src/deploy/getResourceAccess.ts +++ b/packages/cli/src/deploy/getResourceAccess.ts @@ -1,11 +1,11 @@ -import { Client, parseAbiItem, Hex, Address, getAddress } from "viem"; +import { Client, Hex, Address, getAddress } from "viem"; import { WorldDeploy } from "./common"; import { debug } from "./debug"; -import { storeSpliceStaticDataEvent } from "@latticexyz/store"; -import { getLogs } from "viem/actions"; 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"; export async function getResourceAccess({ client, @@ -14,21 +14,21 @@ export async function getResourceAccess({ readonly client: Client; readonly worldDeploy: WorldDeploy; }): Promise { - // This assumes we only use `ResourceAccess._set(...)`, which is true as of this writing. - // TODO: PR to viem's getLogs to accept topics array so we can filter on all store events and quickly recreate this table's current state - debug("looking up resource access for", worldDeploy.address); - const logs = await getLogs(client, { - strict: true, + const blockLogs = await fetchBlockLogs({ fromBlock: worldDeploy.deployBlock, toBlock: worldDeploy.stateBlock, - address: worldDeploy.address, - // our usage of `ResourceAccess._set(...)` emits a splice instead of set record - // TODO: https://github.com/latticexyz/mud/issues/479 - event: parseAbiItem(storeSpliceStaticDataEvent), - args: { tableId: worldConfig.namespaces.world.tables.ResourceAccess.tableId }, + async getLogs({ fromBlock, toBlock }) { + return getStoreLogs(client, { + address: worldDeploy.address, + fromBlock, + toBlock, + tableId: worldConfig.namespaces.world.tables.ResourceAccess.tableId, + }); + }, }); + const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); const keys = logs.map((log) => decodeKey(getSchemaTypes(getKeySchema(worldConfig.namespaces.world.tables.ResourceAccess)), log.args.keyTuple), diff --git a/packages/cli/src/deploy/getResourceIds.ts b/packages/cli/src/deploy/getResourceIds.ts index 4286d671b2..cd050e5f43 100644 --- a/packages/cli/src/deploy/getResourceIds.ts +++ b/packages/cli/src/deploy/getResourceIds.ts @@ -1,10 +1,9 @@ -import { Client, parseAbiItem, Hex, HttpRequestError } from "viem"; -import { getLogs } from "viem/actions"; -import { storeSpliceStaticDataEvent } from "@latticexyz/store"; +import { Client, Hex } from "viem"; +import { flattenStoreLogs, getStoreLogs } from "@latticexyz/store/internal"; import { WorldDeploy } from "./common"; import { debug } from "./debug"; -import pRetry from "p-retry"; import storeConfig from "@latticexyz/store/mud.config"; +import { fetchBlockLogs } from "@latticexyz/block-logs-stream"; export async function getResourceIds({ client, @@ -13,32 +12,22 @@ export async function getResourceIds({ readonly client: Client; readonly worldDeploy: WorldDeploy; }): Promise { - // This assumes we only use `ResourceIds._setExists(true)`, which is true as of this writing. - // TODO: PR to viem's getLogs to accept topics array so we can filter on all store events and quickly recreate this table's current state - debug("looking up resource IDs for", worldDeploy.address); - const logs = await pRetry( - () => - getLogs(client, { - strict: true, - address: worldDeploy.address, - fromBlock: worldDeploy.deployBlock, - toBlock: worldDeploy.stateBlock, - event: parseAbiItem(storeSpliceStaticDataEvent), - args: { tableId: storeConfig.namespaces.store.tables.ResourceIds.tableId }, - }), - { - retries: 3, - onFailedAttempt: async (error) => { - const shouldRetry = - error instanceof HttpRequestError && error.status === 400 && error.message.includes("block is out of range"); - if (!shouldRetry) { - throw error; - } - }, + const blockLogs = await fetchBlockLogs({ + fromBlock: worldDeploy.deployBlock, + toBlock: worldDeploy.stateBlock, + async getLogs({ fromBlock, toBlock }) { + return getStoreLogs(client, { + address: worldDeploy.address, + fromBlock, + toBlock, + tableId: storeConfig.namespaces.store.tables.ResourceIds.tableId, + }); }, - ); + }); + const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); + const resourceIds = logs.map((log) => log.args.keyTuple[0]); debug("found", resourceIds.length, "resource IDs for", worldDeploy.address); diff --git a/packages/cli/src/deploy/getTables.ts b/packages/cli/src/deploy/getTables.ts index 91eb894558..f2624dc112 100644 --- a/packages/cli/src/deploy/getTables.ts +++ b/packages/cli/src/deploy/getTables.ts @@ -1,9 +1,7 @@ -import { Client, parseAbiItem, decodeAbiParameters, parseAbiParameters } from "viem"; +import { Client, decodeAbiParameters, parseAbiParameters } from "viem"; import { hexToResource } from "@latticexyz/common"; import { WorldDeploy } from "./common"; import { debug } from "./debug"; -import { storeSetRecordEvent } from "@latticexyz/store"; -import { getLogs } from "viem/actions"; import { decodeKey, decodeValueArgs, @@ -14,6 +12,8 @@ import { } 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"; // TODO: add label and namespaceLabel once we register it onchain type DeployedTable = Omit; @@ -25,21 +25,21 @@ export async function getTables({ readonly client: Client; readonly worldDeploy: WorldDeploy; }): Promise[]> { - // This assumes we only use `Tables._set(...)`, which is true as of this writing. - // TODO: PR to viem's getLogs to accept topics array so we can filter on all store events and quickly recreate this table's current state - // TODO: consider moving this to a batched getRecord for Tables table - debug("looking up tables for", worldDeploy.address); - const logs = await getLogs(client, { - strict: true, - // this may fail for certain RPC providers with block range limits - // if so, could potentially use our fetchLogs helper (which does pagination) + + const blockLogs = await fetchBlockLogs({ fromBlock: worldDeploy.deployBlock, toBlock: worldDeploy.stateBlock, - address: worldDeploy.address, - event: parseAbiItem(storeSetRecordEvent), - args: { tableId: storeConfig.namespaces.store.tables.Tables.tableId }, + async getLogs({ fromBlock, toBlock }) { + return getStoreLogs(client, { + address: worldDeploy.address, + fromBlock, + toBlock, + tableId: storeConfig.namespaces.store.tables.Tables.tableId, + }); + }, }); + const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs)); // TODO: combine with store-sync logToTable and export from somewhere const tables = logs.map((log): DeployedTable => { diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 51248832f4..00d17b5735 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -7,6 +7,7 @@ export * from "./getBurnerPrivateKey"; export * from "./getNonceManager"; export * from "./getNonceManagerId"; export * from "./hexToResource"; +export * from "./logSort"; export * from "./LruMap"; export * from "./readHex"; export * from "./resourceToLabel"; diff --git a/packages/common/src/logSort.test.ts b/packages/common/src/logSort.test.ts new file mode 100644 index 0000000000..e42fa534dd --- /dev/null +++ b/packages/common/src/logSort.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect } from "vitest"; +import { logSort } from "./logSort"; + +describe("logSort", () => { + it("sorts logs", () => { + const logs = [ + { + blockNumber: 1n, + logIndex: 4, + }, + { + blockNumber: 5n, + logIndex: 0, + }, + { + blockNumber: 1n, + logIndex: 0, + }, + { + blockNumber: 1n, + logIndex: 2, + }, + { + blockNumber: 3n, + logIndex: 3, + }, + { + blockNumber: null, + logIndex: null, + }, + { + // both values should be null for pending blocks, + // but we'll include this as a test case anyway + blockNumber: 3n, + logIndex: null, + }, + { + // both values should be null for pending blocks, + // but we'll include this as a test case anyway + blockNumber: null, + logIndex: 1, + }, + ]; + + logs.sort(logSort); + + expect(logs).toMatchInlineSnapshot(` + [ + { + "blockNumber": 1n, + "logIndex": 0, + }, + { + "blockNumber": 1n, + "logIndex": 2, + }, + { + "blockNumber": 1n, + "logIndex": 4, + }, + { + "blockNumber": 3n, + "logIndex": 3, + }, + { + "blockNumber": 3n, + "logIndex": null, + }, + { + "blockNumber": 5n, + "logIndex": 0, + }, + { + "blockNumber": null, + "logIndex": 1, + }, + { + "blockNumber": null, + "logIndex": null, + }, + ] + `); + }); +}); diff --git a/packages/common/src/logSort.ts b/packages/common/src/logSort.ts new file mode 100644 index 0000000000..9c8927dc0c --- /dev/null +++ b/packages/common/src/logSort.ts @@ -0,0 +1,16 @@ +type PartialLog = { readonly blockNumber: bigint | null; readonly logIndex: number | null }; + +export function logSort(a: PartialLog, b: PartialLog): number { + if (a.blockNumber === b.blockNumber) { + if (a.logIndex === b.logIndex) return 0; + if (a.logIndex == null) return 1; + if (b.logIndex == null) return -1; + return a.logIndex - b.logIndex; + } + + if (a.blockNumber == null) return 1; + if (b.blockNumber == null) return -1; + if (a.blockNumber > b.blockNumber) return 1; + if (a.blockNumber < b.blockNumber) return -1; + return 0; +} diff --git a/packages/store-sync/src/fetchAndStoreLogs.ts b/packages/store-sync/src/fetchAndStoreLogs.ts index 76a5f734e5..6c7a826ed6 100644 --- a/packages/store-sync/src/fetchAndStoreLogs.ts +++ b/packages/store-sync/src/fetchAndStoreLogs.ts @@ -12,7 +12,7 @@ export async function* fetchAndStoreLogs({ logFilter, ...fetchLogsOptions }: FetchAndStoreLogsOptions): AsyncGenerator { - for await (const { logs, toBlock } of fetchLogs(fetchLogsOptions)) { + for await (const { logs, toBlock } of fetchLogs(fetchLogsOptions)) { const blocks = groupLogsByBlockNumber(logFilter ? logs.filter(logFilter) : logs, toBlock); for (const block of blocks) { await storageAdapter(block); diff --git a/packages/store-sync/test/logsToBlocks.ts b/packages/store-sync/test/logsToBlocks.ts index 2cfbcdf50e..d219bc87b9 100644 --- a/packages/store-sync/test/logsToBlocks.ts +++ b/packages/store-sync/test/logsToBlocks.ts @@ -14,8 +14,7 @@ export function logsToBlocks( topics: log.topics as [Hex, ...Hex[]], strict: true, }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return formatLog(log as any as RpcLog, { args, eventName: eventName as string }) as StoreEventsLog; + return formatLog(log as RpcLog, { args, eventName: eventName as string }) as StoreEventsLog; }), ); } diff --git a/packages/store/ts/common.ts b/packages/store/ts/common.ts index 74f90e9ca5..4205c5d1d3 100644 --- a/packages/store/ts/common.ts +++ b/packages/store/ts/common.ts @@ -28,3 +28,9 @@ export type Tables = { export type SchemaToPrimitives = { readonly [key in keyof schema]: SchemaAbiTypeToPrimitiveType; }; + +export const emptyRecord = { + staticData: "0x", + encodedLengths: "0x", + dynamicData: "0x", +} as const; diff --git a/packages/store/ts/debug.ts b/packages/store/ts/debug.ts new file mode 100644 index 0000000000..46f225860a --- /dev/null +++ b/packages/store/ts/debug.ts @@ -0,0 +1,7 @@ +import createDebug from "debug"; + +export const debug = createDebug("mud:store"); +debug.log = console.debug.bind(console); + +export const error = createDebug("mud:store"); +error.log = console.error.bind(console); diff --git a/packages/store/ts/exports/internal.ts b/packages/store/ts/exports/internal.ts index 42b6909822..656d9b3a80 100644 --- a/packages/store/ts/exports/internal.ts +++ b/packages/store/ts/exports/internal.ts @@ -1 +1,3 @@ export * from "../common"; +export * from "../getStoreLogs"; +export * from "../flattenStoreLogs"; diff --git a/packages/store/ts/flattenStoreLogs.test.ts b/packages/store/ts/flattenStoreLogs.test.ts new file mode 100644 index 0000000000..05c0ca5c34 --- /dev/null +++ b/packages/store/ts/flattenStoreLogs.test.ts @@ -0,0 +1,183 @@ +/* eslint-disable max-len */ +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { getStoreLogs } from "./getStoreLogs"; +import { flattenStoreLogs } from "./flattenStoreLogs"; +import { snapshotAnvilState, testClient } from "../../../test-setup/common"; +import { deployMockGame } from "../../../test-setup/mockGame"; +import { summarizeLogs } from "./test/summarizeLogs"; + +describe("flattenStoreLogs", async () => { + beforeAll(async () => { + const resetAnvilState = await snapshotAnvilState(); + await deployMockGame(); + return resetAnvilState; + }); + beforeEach(snapshotAnvilState); + + it("flattens store logs", async () => { + const logs = await getStoreLogs(testClient, { fromBlock: "earliest", toBlock: "latest" }); + const flattenedLogs = flattenStoreLogs(logs); + + expect(summarizeLogs(flattenedLogs)).toMatchInlineSnapshot(` + [ + "Store_SetRecord world__InitModuleAddres ()", + "Store_SetRecord store__Tables (0x746273746f72650000000000000000005461626c657300000000000000000000)", + "Store_SetRecord store__Tables (0x746273746f72650000000000000000005265736f757263654964730000000000)", + "Store_SetRecord store__ResourceIds (0x746273746f72650000000000000000005461626c657300000000000000000000)", + "Store_SetRecord store__ResourceIds (0x746273746f72650000000000000000005265736f757263654964730000000000)", + "Store_SetRecord store__Tables (0x746273746f726500000000000000000053746f7265486f6f6b73000000000000)", + "Store_SetRecord store__ResourceIds (0x746273746f726500000000000000000053746f7265486f6f6b73000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000004e616d6573706163654f776e65720000)", + "Store_SetRecord store__ResourceIds (0x7462776f726c640000000000000000004e616d6573706163654f776e65720000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000042616c616e6365730000000000000000)", + "Store_SetRecord store__ResourceIds (0x7462776f726c6400000000000000000042616c616e6365730000000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c64000000000000000000496e7374616c6c65644d6f64756c6573)", + "Store_SetRecord store__ResourceIds (0x7462776f726c64000000000000000000496e7374616c6c65644d6f64756c6573)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000005573657244656c65676174696f6e436f)", + "Store_SetRecord store__ResourceIds (0x7462776f726c640000000000000000005573657244656c65676174696f6e436f)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000004e616d65737061636544656c65676174)", + "Store_SetRecord store__ResourceIds (0x7462776f726c640000000000000000004e616d65737061636544656c65676174)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000005265736f757263654163636573730000)", + "Store_SetRecord store__ResourceIds (0x7462776f726c640000000000000000005265736f757263654163636573730000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d73000000000000000000)", + "Store_SetRecord store__ResourceIds (0x7462776f726c6400000000000000000053797374656d73000000000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72)", + "Store_SetRecord store__ResourceIds (0x7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72)", + "Store_SetRecord store__Tables (0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572)", + "Store_SetRecord store__ResourceIds (0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d486f6f6b730000000000)", + "Store_SetRecord store__ResourceIds (0x7462776f726c6400000000000000000053797374656d486f6f6b730000000000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d52656769737472790000)", + "Store_SetRecord store__ResourceIds (0x7462776f726c6400000000000000000053797374656d52656769737472790000)", + "Store_SetRecord store__Tables (0x7462776f726c64000000000000000000496e69744d6f64756c65416464726573)", + "Store_SetRecord store__ResourceIds (0x7462776f726c64000000000000000000496e69744d6f64756c65416464726573)", + "Store_SetRecord store__ResourceIds (0x6e73000000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord store__ResourceIds (0x6e7373746f726500000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__NamespaceOwner (0x6e7373746f726500000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__ResourceAccess (0x6e7373746f726500000000000000000000000000000000000000000000000000,0x000000000000000000000000573802f86c51b61d7cf620952217ec6ce0537d2e)", + "Store_SetRecord store__ResourceIds (0x6e73776f726c6400000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__NamespaceOwner (0x6e73776f726c6400000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__ResourceAccess (0x6e73776f726c6400000000000000000000000000000000000000000000000000,0x000000000000000000000000573802f86c51b61d7cf620952217ec6ce0537d2e)", + "Store_SetRecord store__ResourceIds (0x737900000000000000000000000000004163636573734d616e6167656d656e74)", + "Store_SetRecord world__Systems (0x737900000000000000000000000000004163636573734d616e6167656d656e74)", + "Store_SetRecord world__SystemRegistry (0x00000000000000000000000017ffdeff94ed0b80c493a179d4b3b09d6d71f627)", + "Store_SetRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x00000000000000000000000017ffdeff94ed0b80c493a179d4b3b09d6d71f627)", + "Store_SetRecord store__ResourceIds (0x7379000000000000000000000000000042616c616e63655472616e7366657200)", + "Store_SetRecord world__Systems (0x7379000000000000000000000000000042616c616e63655472616e7366657200)", + "Store_SetRecord world__SystemRegistry (0x000000000000000000000000a274b9a7e743cd8df3c6fd0abd47ed55fc943bc3)", + "Store_SetRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000a274b9a7e743cd8df3c6fd0abd47ed55fc943bc3)", + "Store_SetRecord store__ResourceIds (0x73790000000000000000000000000000426174636843616c6c00000000000000)", + "Store_SetRecord world__Systems (0x73790000000000000000000000000000426174636843616c6c00000000000000)", + "Store_SetRecord world__SystemRegistry (0x00000000000000000000000053e5c08d82a377167069ade46d087ab753538608)", + "Store_SetRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x00000000000000000000000053e5c08d82a377167069ade46d087ab753538608)", + "Store_SetRecord store__ResourceIds (0x73790000000000000000000000000000526567697374726174696f6e00000000)", + "Store_SetRecord world__Systems (0x73790000000000000000000000000000526567697374726174696f6e00000000)", + "Store_SetRecord world__SystemRegistry (0x000000000000000000000000d416f26aafcaaeca50b0dc35bd023e7286be2961)", + "Store_SetRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000d416f26aafcaaeca50b0dc35bd023e7286be2961)", + "Store_SetRecord world__FunctionSelector (0x40554c3a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x40554c3a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x8d53b20800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8d53b20800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xef5d6bbb00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xef5d6bbb00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x219adc2e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x219adc2e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xc9c85a6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xc9c85a6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x45afd19900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x45afd19900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xce5e8dd900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xce5e8dd900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x8fc8cf7e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8fc8cf7e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x8da798da00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8da798da00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x0ba51f4900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x0ba51f4900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x530f4b6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x530f4b6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x0560912900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x0560912900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xb29e408900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xb29e408900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xd5f8337f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xd5f8337f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xa92813ad00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xa92813ad00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x3350b6a900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x3350b6a900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x26d9810200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x26d9810200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x6548a90a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x6548a90a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x1d2257ba00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x1d2257ba00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xcdc938c500000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xcdc938c500000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xbfdfaff700000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xbfdfaff700000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xaa66e9c800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xaa66e9c800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__InstalledModules (0x000000000000000000000000da4e062e8c69d39d9472945232a53f579904ac45,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_SetRecord world__NamespaceOwner (0x6e73000000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266)", + "Store_SetRecord store__Tables (0x74620000000000000000000000000000506f736974696f6e0000000000000000)", + "Store_SetRecord store__ResourceIds (0x74620000000000000000000000000000506f736974696f6e0000000000000000)", + "Store_SetRecord store__Tables (0x746200000000000000000000000000004865616c746800000000000000000000)", + "Store_SetRecord store__ResourceIds (0x746200000000000000000000000000004865616c746800000000000000000000)", + "Store_SetRecord store__Tables (0x74620000000000000000000000000000496e76656e746f727900000000000000)", + "Store_SetRecord store__ResourceIds (0x74620000000000000000000000000000496e76656e746f727900000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000053636f72650000000000000000000000)", + "Store_SetRecord store__ResourceIds (0x7462000000000000000000000000000053636f72650000000000000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000057696e6e657200000000000000000000)", + "Store_SetRecord store__ResourceIds (0x7462000000000000000000000000000057696e6e657200000000000000000000)", + "Store_SetRecord store__Tables (0x746200000000000000000000000000005465727261696e000000000000000000)", + "Store_SetRecord store__ResourceIds (0x746200000000000000000000000000005465727261696e000000000000000000)", + "Store_SetRecord store__ResourceIds (0x737900000000000000000000000000004d6f766553797374656d000000000000)", + "Store_SetRecord world__Systems (0x737900000000000000000000000000004d6f766553797374656d000000000000)", + "Store_SetRecord world__SystemRegistry (0x000000000000000000000000909d87ff2af6abace4fe66171b9622bc10305c3c)", + "Store_SetRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000909d87ff2af6abace4fe66171b9622bc10305c3c)", + "Store_SetRecord world__FunctionSelector (0xb591186e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xb591186e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000043616c6c576974685369676e61747572)", + "Store_SetRecord store__ResourceIds (0x7462000000000000000000000000000043616c6c576974685369676e61747572)", + "Store_SetRecord store__ResourceIds (0x7379000000000000000000000000000044656c65676174696f6e000000000000)", + "Store_SetRecord world__Systems (0x7379000000000000000000000000000044656c65676174696f6e000000000000)", + "Store_SetRecord world__SystemRegistry (0x000000000000000000000000d09016b5b55461012d558a0945e9e7ce48bbad90)", + "Store_SetRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000d09016b5b55461012d558a0945e9e7ce48bbad90)", + "Store_SetRecord world__FunctionSelector (0x1fae630800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x1fae630800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__InstalledModules (0x000000000000000000000000576a2cef28fbe49215143ae4d87e03ea1e99e37a,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_SetRecord store__ResourceIds (0x6e736d6574616461746100000000000000000000000000000000000000000000)", + "Store_SetRecord store__Tables (0x74626d657461646174610000000000005265736f757263655461670000000000)", + "Store_SetRecord store__ResourceIds (0x74626d657461646174610000000000005265736f757263655461670000000000)", + "Store_SetRecord store__ResourceIds (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", + "Store_SetRecord world__Systems (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", + "Store_SetRecord world__SystemRegistry (0x0000000000000000000000000d0a0ad663793e3d078fec50a85cf32d95c3a3c4)", + "Store_SetRecord world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x0000000000000000000000000d0a0ad663793e3d078fec50a85cf32d95c3a3c4)", + "Store_SetRecord world__FunctionSelector (0xff66f05f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xc6972e9300000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xff66f05f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xefc1704200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x116e68f200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xefc1704200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xf128760200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__NamespaceOwner (0x6e736d6574616461746100000000000000000000000000000000000000000000)", + "Store_SetRecord world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266)", + "Store_SetRecord world__InstalledModules (0x0000000000000000000000002ff959c7d78a64356c28bcf5f6e3cd56f1463901,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_SetRecord metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x6162690000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x776f726c64416269000000000000000000000000000000000000000000000000)", + "Store_SetRecord Position (0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e)", + "Store_SetRecord Health (0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e)", + "Store_SetRecord Position (0x000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6)", + "Store_SetRecord Health (0x000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6)", + "Store_SetRecord Position (0x000000000000000000000000078cf0753dd50f7c56f20b3ae02719ea199be2eb)", + "Store_SetRecord Health (0x000000000000000000000000078cf0753dd50f7c56f20b3ae02719ea199be2eb)", + "Store_SetRecord Position (0x000000000000000000000000dba86119a787422c593cef119e40887f396024e2)", + "Store_SetRecord Terrain (0x0000000000000000000000000000000000000000000000000000000000000003,0x0000000000000000000000000000000000000000000000000000000000000005)", + ] + `); + }); +}); diff --git a/packages/store/ts/flattenStoreLogs.ts b/packages/store/ts/flattenStoreLogs.ts new file mode 100644 index 0000000000..bd50b8ea17 --- /dev/null +++ b/packages/store/ts/flattenStoreLogs.ts @@ -0,0 +1,65 @@ +import { Log, size } from "viem"; +import { storeEventsAbi } from "./storeEventsAbi"; +import { emptyRecord } from "./common"; +import { StoreLog, StoreSetRecordLog } from "./storeLog"; +import { logSort, spliceHex } from "@latticexyz/common"; + +function getKey(log: Log) { + return [log.address, log.args.tableId, log.args.keyTuple.join(",")].join(":"); +} + +export function flattenStoreLogs(logs: StoreLog[]): StoreSetRecordLog[] { + const sortedLogs = logs.slice().sort(logSort); + const records = new Map(); + + for (const log of sortedLogs) { + const key = getKey(log); + + if (log.eventName === "Store_SetRecord") { + // maps preserve order, so always delete then set so the record gets + // added to the end of the map, thus preserving the log order + records.delete(key); + records.set(key, log); + } else if (log.eventName === "Store_SpliceStaticData") { + const previousRecord = records.get(key); + const { staticData, encodedLengths, dynamicData } = previousRecord?.args ?? emptyRecord; + const nextRecord = { + ...log, + eventName: "Store_SetRecord", + args: { + tableId: log.args.tableId, + keyTuple: log.args.keyTuple, + staticData: spliceHex(staticData, log.args.start, size(log.args.data), log.args.data), + encodedLengths, + dynamicData, + }, + } satisfies StoreSetRecordLog; + // maps preserve order, so always delete then set so the record gets + // added to the end of the map, thus preserving the log order + records.delete(key); + records.set(key, nextRecord); + } else if (log.eventName === "Store_SpliceDynamicData") { + const previousRecord = records.get(key); + const { staticData, dynamicData } = previousRecord?.args ?? emptyRecord; + const nextRecord = { + ...log, + eventName: "Store_SetRecord", + args: { + tableId: log.args.tableId, + keyTuple: log.args.keyTuple, + staticData, + encodedLengths: log.args.encodedLengths, + dynamicData: spliceHex(dynamicData, log.args.start, log.args.deleteCount, log.args.data), + }, + } satisfies StoreSetRecordLog; + // maps preserve order, so always delete then set so the record gets + // added to the end of the map, thus preserving the log order + records.delete(key); + records.set(key, nextRecord); + } else if (log.eventName === "Store_DeleteRecord") { + records.delete(key); + } + } + + return Array.from(records.values()); +} diff --git a/packages/store/ts/getStoreLogs.test.ts b/packages/store/ts/getStoreLogs.test.ts new file mode 100644 index 0000000000..ccae995140 --- /dev/null +++ b/packages/store/ts/getStoreLogs.test.ts @@ -0,0 +1,326 @@ +/* eslint-disable max-len */ +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { getStoreLogs } from "./getStoreLogs"; +import config from "../mud.config"; +import { snapshotAnvilState, testClient } from "../../../test-setup/common"; +import { deployMockGame } from "../../../test-setup/mockGame"; +import { summarizeLogs } from "./test/summarizeLogs"; + +describe("getStoreLogs", async () => { + beforeAll(async () => { + const resetAnvilState = await snapshotAnvilState(); + await deployMockGame(); + return resetAnvilState; + }); + beforeEach(snapshotAnvilState); + + it("fetches only store logs", async () => { + const logs = await getStoreLogs(testClient, { fromBlock: "earliest", toBlock: "latest" }); + expect(summarizeLogs(logs)).toMatchInlineSnapshot(` + [ + "Store_SpliceStaticData world__InitModuleAddres ()", + "Store_SetRecord store__Tables (0x746273746f72650000000000000000005461626c657300000000000000000000)", + "Store_SetRecord store__Tables (0x746273746f72650000000000000000005265736f757263654964730000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f72650000000000000000005461626c657300000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f72650000000000000000005265736f757263654964730000000000)", + "Store_SetRecord store__Tables (0x746273746f726500000000000000000053746f7265486f6f6b73000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f726500000000000000000053746f7265486f6f6b73000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000004e616d6573706163654f776e65720000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000004e616d6573706163654f776e65720000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000042616c616e6365730000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000042616c616e6365730000000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c64000000000000000000496e7374616c6c65644d6f64756c6573)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c64000000000000000000496e7374616c6c65644d6f64756c6573)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000005573657244656c65676174696f6e436f)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000005573657244656c65676174696f6e436f)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000004e616d65737061636544656c65676174)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000004e616d65737061636544656c65676174)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000005265736f757263654163636573730000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000005265736f757263654163636573730000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d73000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d73000000000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72)", + "Store_SetRecord store__Tables (0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d486f6f6b730000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d486f6f6b730000000000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d52656769737472790000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d52656769737472790000)", + "Store_SetRecord store__Tables (0x7462776f726c64000000000000000000496e69744d6f64756c65416464726573)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c64000000000000000000496e69744d6f64756c65416464726573)", + "Store_SpliceStaticData store__ResourceIds (0x6e73000000000000000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__NamespaceOwner (0x6e73000000000000000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000573802f86c51b61d7cf620952217ec6ce0537d2e)", + "Store_SpliceStaticData store__ResourceIds (0x6e7373746f726500000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__NamespaceOwner (0x6e7373746f726500000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__ResourceAccess (0x6e7373746f726500000000000000000000000000000000000000000000000000,0x000000000000000000000000573802f86c51b61d7cf620952217ec6ce0537d2e)", + "Store_SpliceStaticData store__ResourceIds (0x6e73776f726c6400000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__NamespaceOwner (0x6e73776f726c6400000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73776f726c6400000000000000000000000000000000000000000000000000,0x000000000000000000000000573802f86c51b61d7cf620952217ec6ce0537d2e)", + "Store_SpliceStaticData store__ResourceIds (0x737900000000000000000000000000004163636573734d616e6167656d656e74)", + "Store_SetRecord world__Systems (0x737900000000000000000000000000004163636573734d616e6167656d656e74)", + "Store_SpliceStaticData world__SystemRegistry (0x00000000000000000000000017ffdeff94ed0b80c493a179d4b3b09d6d71f627)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x00000000000000000000000017ffdeff94ed0b80c493a179d4b3b09d6d71f627)", + "Store_SpliceStaticData store__ResourceIds (0x7379000000000000000000000000000042616c616e63655472616e7366657200)", + "Store_SetRecord world__Systems (0x7379000000000000000000000000000042616c616e63655472616e7366657200)", + "Store_SpliceStaticData world__SystemRegistry (0x000000000000000000000000a274b9a7e743cd8df3c6fd0abd47ed55fc943bc3)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000a274b9a7e743cd8df3c6fd0abd47ed55fc943bc3)", + "Store_SpliceStaticData store__ResourceIds (0x73790000000000000000000000000000426174636843616c6c00000000000000)", + "Store_SetRecord world__Systems (0x73790000000000000000000000000000426174636843616c6c00000000000000)", + "Store_SpliceStaticData world__SystemRegistry (0x00000000000000000000000053e5c08d82a377167069ade46d087ab753538608)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x00000000000000000000000053e5c08d82a377167069ade46d087ab753538608)", + "Store_SpliceStaticData store__ResourceIds (0x73790000000000000000000000000000526567697374726174696f6e00000000)", + "Store_SetRecord world__Systems (0x73790000000000000000000000000000526567697374726174696f6e00000000)", + "Store_SpliceStaticData world__SystemRegistry (0x000000000000000000000000d416f26aafcaaeca50b0dc35bd023e7286be2961)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000d416f26aafcaaeca50b0dc35bd023e7286be2961)", + "Store_SetRecord world__FunctionSelector (0x40554c3a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x40554c3a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x40554c3a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x8d53b20800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8d53b20800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8d53b20800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xef5d6bbb00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xef5d6bbb00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xef5d6bbb00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x219adc2e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x219adc2e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x219adc2e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xc9c85a6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xc9c85a6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xc9c85a6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x45afd19900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x45afd19900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x45afd19900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xce5e8dd900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xce5e8dd900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xce5e8dd900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x8fc8cf7e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8fc8cf7e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8fc8cf7e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x8da798da00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8da798da00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x8da798da00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x0ba51f4900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x0ba51f4900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x0ba51f4900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x530f4b6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x530f4b6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x530f4b6000000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x0560912900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x0560912900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x0560912900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xb29e408900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xb29e408900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xb29e408900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xd5f8337f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xd5f8337f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xd5f8337f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xa92813ad00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xa92813ad00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xa92813ad00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x3350b6a900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x3350b6a900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x3350b6a900000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x26d9810200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x26d9810200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x26d9810200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x6548a90a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x6548a90a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x6548a90a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x1d2257ba00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x1d2257ba00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x1d2257ba00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xcdc938c500000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xcdc938c500000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xcdc938c500000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xbfdfaff700000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xbfdfaff700000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xbfdfaff700000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xaa66e9c800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xaa66e9c800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xaa66e9c800000000000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__InstalledModules (0x000000000000000000000000da4e062e8c69d39d9472945232a53f579904ac45,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_SpliceStaticData world__NamespaceOwner (0x6e73000000000000000000000000000000000000000000000000000000000000)", + "Store_DeleteRecord world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000573802f86c51b61d7cf620952217ec6ce0537d2e)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266)", + "Store_SetRecord store__Tables (0x74620000000000000000000000000000506f736974696f6e0000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74620000000000000000000000000000506f736974696f6e0000000000000000)", + "Store_SetRecord store__Tables (0x746200000000000000000000000000004865616c746800000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746200000000000000000000000000004865616c746800000000000000000000)", + "Store_SetRecord store__Tables (0x74620000000000000000000000000000496e76656e746f727900000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74620000000000000000000000000000496e76656e746f727900000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000053636f72650000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000053636f72650000000000000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000057696e6e657200000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000057696e6e657200000000000000000000)", + "Store_SetRecord store__Tables (0x746200000000000000000000000000005465727261696e000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746200000000000000000000000000005465727261696e000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x737900000000000000000000000000004d6f766553797374656d000000000000)", + "Store_SetRecord world__Systems (0x737900000000000000000000000000004d6f766553797374656d000000000000)", + "Store_SpliceStaticData world__SystemRegistry (0x000000000000000000000000909d87ff2af6abace4fe66171b9622bc10305c3c)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000909d87ff2af6abace4fe66171b9622bc10305c3c)", + "Store_SetRecord world__FunctionSelector (0xb591186e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xb591186e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xb591186e00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000043616c6c576974685369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000043616c6c576974685369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x7379000000000000000000000000000044656c65676174696f6e000000000000)", + "Store_SetRecord world__Systems (0x7379000000000000000000000000000044656c65676174696f6e000000000000)", + "Store_SpliceStaticData world__SystemRegistry (0x000000000000000000000000d09016b5b55461012d558a0945e9e7ce48bbad90)", + "Store_SpliceStaticData world__ResourceAccess (0x6e73000000000000000000000000000000000000000000000000000000000000,0x000000000000000000000000d09016b5b55461012d558a0945e9e7ce48bbad90)", + "Store_SetRecord world__FunctionSelector (0x1fae630800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x1fae630800000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x1fae630800000000000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__InstalledModules (0x000000000000000000000000576a2cef28fbe49215143ae4d87e03ea1e99e37a,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_SpliceStaticData store__ResourceIds (0x6e736d6574616461746100000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__NamespaceOwner (0x6e736d6574616461746100000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x0000000000000000000000002ff959c7d78a64356c28bcf5f6e3cd56f1463901)", + "Store_SetRecord store__Tables (0x74626d657461646174610000000000005265736f757263655461670000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74626d657461646174610000000000005265736f757263655461670000000000)", + "Store_SpliceStaticData store__ResourceIds (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", + "Store_SetRecord world__Systems (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", + "Store_SpliceStaticData world__SystemRegistry (0x0000000000000000000000000d0a0ad663793e3d078fec50a85cf32d95c3a3c4)", + "Store_SpliceStaticData world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x0000000000000000000000000d0a0ad663793e3d078fec50a85cf32d95c3a3c4)", + "Store_SetRecord world__FunctionSelector (0xff66f05f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xc6972e9300000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xff66f05f00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0xefc1704200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x116e68f200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xefc1704200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSelector (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0xf128760200000000000000000000000000000000000000000000000000000000)", + "Store_SetRecord world__FunctionSignatur (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData world__NamespaceOwner (0x6e736d6574616461746100000000000000000000000000000000000000000000)", + "Store_DeleteRecord world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x0000000000000000000000002ff959c7d78a64356c28bcf5f6e3cd56f1463901)", + "Store_SpliceStaticData world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266)", + "Store_SpliceStaticData world__InstalledModules (0x0000000000000000000000002ff959c7d78a64356c28bcf5f6e3cd56f1463901,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_SpliceDynamicData metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x6162690000000000000000000000000000000000000000000000000000000000)", + "Store_SpliceDynamicData metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x776f726c64416269000000000000000000000000000000000000000000000000)", + "Store_SetRecord Position (0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e)", + "Store_SpliceStaticData Health (0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e)", + "Store_SetRecord Position (0x000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6)", + "Store_SpliceStaticData Health (0x000000000000000000000000328809bc894f92807417d2dad6b7c998c1afdac6)", + "Store_SetRecord Position (0x000000000000000000000000078cf0753dd50f7c56f20b3ae02719ea199be2eb)", + "Store_SpliceStaticData Health (0x000000000000000000000000078cf0753dd50f7c56f20b3ae02719ea199be2eb)", + "Store_SetRecord Position (0x000000000000000000000000dba86119a787422c593cef119e40887f396024e2)", + "Store_SpliceStaticData Terrain (0x0000000000000000000000000000000000000000000000000000000000000003,0x0000000000000000000000000000000000000000000000000000000000000005)", + ] + `); + }); + + it("fetches only store logs for a specific table", async () => { + const logs = await getStoreLogs(testClient, { + fromBlock: "earliest", + toBlock: "latest", + tableId: config.tables.store__ResourceIds.tableId, + }); + expect(summarizeLogs(logs)).toMatchInlineSnapshot(` + [ + "Store_SpliceStaticData store__ResourceIds (0x746273746f72650000000000000000005461626c657300000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f72650000000000000000005265736f757263654964730000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f726500000000000000000053746f7265486f6f6b73000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000004e616d6573706163654f776e65720000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000042616c616e6365730000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c64000000000000000000496e7374616c6c65644d6f64756c6573)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000005573657244656c65676174696f6e436f)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000004e616d65737061636544656c65676174)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000005265736f757263654163636573730000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d73000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72)", + "Store_SpliceStaticData store__ResourceIds (0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d486f6f6b730000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d52656769737472790000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c64000000000000000000496e69744d6f64756c65416464726573)", + "Store_SpliceStaticData store__ResourceIds (0x6e73000000000000000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x6e7373746f726500000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x6e73776f726c6400000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x737900000000000000000000000000004163636573734d616e6167656d656e74)", + "Store_SpliceStaticData store__ResourceIds (0x7379000000000000000000000000000042616c616e63655472616e7366657200)", + "Store_SpliceStaticData store__ResourceIds (0x73790000000000000000000000000000426174636843616c6c00000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x73790000000000000000000000000000526567697374726174696f6e00000000)", + "Store_SpliceStaticData store__ResourceIds (0x74620000000000000000000000000000506f736974696f6e0000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746200000000000000000000000000004865616c746800000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74620000000000000000000000000000496e76656e746f727900000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000053636f72650000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000057696e6e657200000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746200000000000000000000000000005465727261696e000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x737900000000000000000000000000004d6f766553797374656d000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000043616c6c576974685369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x7379000000000000000000000000000044656c65676174696f6e000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x6e736d6574616461746100000000000000000000000000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74626d657461646174610000000000005265736f757263655461670000000000)", + "Store_SpliceStaticData store__ResourceIds (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", + ] + `); + }); + + it("fetches only store logs for specific tables", async () => { + const logs = await getStoreLogs(testClient, { + fromBlock: "earliest", + toBlock: "latest", + tableId: [config.tables.store__ResourceIds.tableId, config.tables.store__Tables.tableId], + }); + expect(summarizeLogs(logs)).toMatchInlineSnapshot(` + [ + "Store_SetRecord store__Tables (0x746273746f72650000000000000000005461626c657300000000000000000000)", + "Store_SetRecord store__Tables (0x746273746f72650000000000000000005265736f757263654964730000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f72650000000000000000005461626c657300000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f72650000000000000000005265736f757263654964730000000000)", + "Store_SetRecord store__Tables (0x746273746f726500000000000000000053746f7265486f6f6b73000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746273746f726500000000000000000053746f7265486f6f6b73000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000004e616d6573706163654f776e65720000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000004e616d6573706163654f776e65720000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000042616c616e6365730000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000042616c616e6365730000000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c64000000000000000000496e7374616c6c65644d6f64756c6573)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c64000000000000000000496e7374616c6c65644d6f64756c6573)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000005573657244656c65676174696f6e436f)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000005573657244656c65676174696f6e436f)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000004e616d65737061636544656c65676174)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000004e616d65737061636544656c65676174)", + "Store_SetRecord store__Tables (0x7462776f726c640000000000000000005265736f757263654163636573730000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c640000000000000000005265736f757263654163636573730000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d73000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d73000000000000000000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72)", + "Store_SetRecord store__Tables (0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d486f6f6b730000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d486f6f6b730000000000)", + "Store_SetRecord store__Tables (0x7462776f726c6400000000000000000053797374656d52656769737472790000)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c6400000000000000000053797374656d52656769737472790000)", + "Store_SetRecord store__Tables (0x7462776f726c64000000000000000000496e69744d6f64756c65416464726573)", + "Store_SpliceStaticData store__ResourceIds (0x7462776f726c64000000000000000000496e69744d6f64756c65416464726573)", + "Store_SpliceStaticData store__ResourceIds (0x6e73000000000000000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x6e7373746f726500000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x6e73776f726c6400000000000000000000000000000000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x737900000000000000000000000000004163636573734d616e6167656d656e74)", + "Store_SpliceStaticData store__ResourceIds (0x7379000000000000000000000000000042616c616e63655472616e7366657200)", + "Store_SpliceStaticData store__ResourceIds (0x73790000000000000000000000000000426174636843616c6c00000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x73790000000000000000000000000000526567697374726174696f6e00000000)", + "Store_SetRecord store__Tables (0x74620000000000000000000000000000506f736974696f6e0000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74620000000000000000000000000000506f736974696f6e0000000000000000)", + "Store_SetRecord store__Tables (0x746200000000000000000000000000004865616c746800000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746200000000000000000000000000004865616c746800000000000000000000)", + "Store_SetRecord store__Tables (0x74620000000000000000000000000000496e76656e746f727900000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74620000000000000000000000000000496e76656e746f727900000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000053636f72650000000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000053636f72650000000000000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000057696e6e657200000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000057696e6e657200000000000000000000)", + "Store_SetRecord store__Tables (0x746200000000000000000000000000005465727261696e000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x746200000000000000000000000000005465727261696e000000000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x737900000000000000000000000000004d6f766553797374656d000000000000)", + "Store_SetRecord store__Tables (0x7462000000000000000000000000000043616c6c576974685369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x7462000000000000000000000000000043616c6c576974685369676e61747572)", + "Store_SpliceStaticData store__ResourceIds (0x7379000000000000000000000000000044656c65676174696f6e000000000000)", + "Store_SpliceStaticData store__ResourceIds (0x6e736d6574616461746100000000000000000000000000000000000000000000)", + "Store_SetRecord store__Tables (0x74626d657461646174610000000000005265736f757263655461670000000000)", + "Store_SpliceStaticData store__ResourceIds (0x74626d657461646174610000000000005265736f757263655461670000000000)", + "Store_SpliceStaticData store__ResourceIds (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", + ] + `); + }); +}); diff --git a/packages/store/ts/getStoreLogs.ts b/packages/store/ts/getStoreLogs.ts new file mode 100644 index 0000000000..16d3aeeb77 --- /dev/null +++ b/packages/store/ts/getStoreLogs.ts @@ -0,0 +1,104 @@ +import { + BlockNumber, + BlockTag, + Address, + Log, + Hex, + GetLogsErrorType, + Client, + toEventSelector, + LogTopic, + RpcLog, + numberToHex, + formatLog, + parseEventLogs, +} from "viem"; +import { storeEventsAbi } from "./storeEventsAbi"; +import { debug } from "./debug"; +import { hexToResource, resourceToLabel } from "@latticexyz/common"; + +export type GetStoreLogsParameters< + fromBlock extends BlockNumber | BlockTag | undefined = undefined, + toBlock extends BlockNumber | BlockTag | undefined = undefined, +> = { + /** Store address or list of store addresses from which logs originated */ + address?: Address | Address[] | undefined; + /** Optionally match a specific table ID or list of table IDs */ + tableId?: Hex | Hex[] | undefined; + /** Block number or tag after which to include logs */ + fromBlock?: fromBlock | BlockNumber | BlockTag | undefined; + /** Block number or tag before which to include logs */ + toBlock?: toBlock | BlockNumber | BlockTag | undefined; +}; + +export type GetStoreLogsReturnType< + fromBlock extends BlockNumber | BlockTag | undefined = undefined, + toBlock extends BlockNumber | BlockTag | undefined = undefined, + _pending extends boolean = (fromBlock extends "pending" ? true : false) | (toBlock extends "pending" ? true : false), +> = Log[]; + +export type GetStoreLogsErrorType = GetLogsErrorType; + +/** + * Returns an unordered list of store event logs matching the provided parameters. + * + * @param client - Client to use + * @param parameters - {@link GetStoreLogsParameters} + * @returns A list of event logs. {@link GetStoreLogsReturnType} + * + * @example + * import { createClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { getStoreLogs } from '@latticexyz/store' + * + * const client = createPublicClient({ + * chain: mainnet, + * transport: http(), + * }) + * const storeLogs = await getStoreLogs(client) + */ +export async function getStoreLogs< + fromBlock extends BlockNumber | BlockTag | undefined = undefined, + toBlock extends BlockNumber | BlockTag | undefined = undefined, +>( + client: Client, + { address, tableId, fromBlock, toBlock }: GetStoreLogsParameters = {}, +): Promise> { + /** + * Note that this implementation follows Viem's [`getLogs`][0] action: + * https://github.com/wevm/viem/blob/main/src/actions/public/getLogs.ts + * + * It's adapted to allow filtering by table ID(s), which Viem's `getLogs` + * does not support due to how it builds topics. + */ + + const topics: LogTopic[] = [storeEventsAbi.map(toEventSelector), tableId ?? null]; + + const tableIds = tableId ? (Array.isArray(tableId) ? tableId : [tableId]) : []; + const addresses = address ? (Array.isArray(address) ? address : [address]) : []; + debug( + `getting store logs for ${ + tableIds.length ? tableIds.map(hexToResource).map(resourceToLabel).join(", ") : "all tables" + } at ${addresses.length ? addresses.join(", ") : "any address"}`, + ); + + const logs: RpcLog[] = await client.request({ + method: "eth_getLogs", + params: [ + { + address, + topics, + fromBlock: typeof fromBlock === "bigint" ? numberToHex(fromBlock) : fromBlock, + toBlock: typeof toBlock === "bigint" ? numberToHex(toBlock) : toBlock, + }, + ], + }); + + const formattedLogs = logs.map((log) => formatLog(log)); + return parseEventLogs({ + abi: storeEventsAbi, + args: { tableId }, + logs: formattedLogs, + strict: true, + }) as GetStoreLogsReturnType; +} diff --git a/packages/store/ts/storeEventsAbi.ts b/packages/store/ts/storeEventsAbi.ts index d15b0b3c67..dbe26f3bac 100644 --- a/packages/store/ts/storeEventsAbi.ts +++ b/packages/store/ts/storeEventsAbi.ts @@ -2,6 +2,9 @@ import { parseAbi, AbiEvent } from "abitype"; import { storeEvents } from "./storeEvents"; export const storeEventsAbi = parseAbi(storeEvents) satisfies readonly AbiEvent[]; +export type storeEventsAbi = typeof storeEventsAbi; export type StoreEventsAbi = typeof storeEventsAbi; export type StoreEventsAbiItem = (typeof storeEventsAbi)[number]; + +export type StoreSetRecordEventAbiItem = StoreEventsAbiItem & { readonly name: "Store_SetRecord" }; diff --git a/packages/store/ts/storeLog.ts b/packages/store/ts/storeLog.ts new file mode 100644 index 0000000000..d97a3c59ec --- /dev/null +++ b/packages/store/ts/storeLog.ts @@ -0,0 +1,5 @@ +import { Log } from "viem"; +import { StoreEventsAbiItem, StoreEventsAbi, StoreSetRecordEventAbiItem } from "./storeEventsAbi"; + +export type StoreLog = Log; +export type StoreSetRecordLog = Log; diff --git a/packages/store/ts/test/summarizeLogs.ts b/packages/store/ts/test/summarizeLogs.ts new file mode 100644 index 0000000000..6969863076 --- /dev/null +++ b/packages/store/ts/test/summarizeLogs.ts @@ -0,0 +1,12 @@ +import { logSort, resourceToLabel, hexToResource } from "@latticexyz/common"; +import { StoreLog } from "../storeLog"; + +export function summarizeLogs(logs: StoreLog[]) { + return logs + .slice() + .sort(logSort) + .map( + ({ eventName, args: { tableId, keyTuple } }) => + `${eventName} ${resourceToLabel(hexToResource(tableId))} (${keyTuple})`, + ); +}