diff --git a/.changeset/flat-swans-turn.md b/.changeset/flat-swans-turn.md index b84f3e8e32..47421585b0 100644 --- a/.changeset/flat-swans-turn.md +++ b/.changeset/flat-swans-turn.md @@ -2,4 +2,4 @@ "@latticexyz/store-sync": patch --- -Refactored `syncToZustand` to use new Store config under the hood, removing compatibility layers and improving performance. +Refactored package to use the new Store/World configs under the hood, removing compatibility layers and improving performance. diff --git a/.changeset/fluffy-experts-heal.md b/.changeset/fluffy-experts-heal.md new file mode 100644 index 0000000000..1c18a6542e --- /dev/null +++ b/.changeset/fluffy-experts-heal.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store-indexer": patch +--- + +Updated return values to match updated types in `@latticexyz/store-sync`. diff --git a/.changeset/real-pigs-work.md b/.changeset/real-pigs-work.md deleted file mode 100644 index 50c5f5426e..0000000000 --- a/.changeset/real-pigs-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@latticexyz/store-sync": patch ---- - -Refactored `syncToRecs` to use new Store config under the hood, removing compatibility layers and improving performance. diff --git a/packages/protocol-parser/src/exports/internal.ts b/packages/protocol-parser/src/exports/internal.ts index 05f692c0ad..6f2d68af6b 100644 --- a/packages/protocol-parser/src/exports/internal.ts +++ b/packages/protocol-parser/src/exports/internal.ts @@ -29,3 +29,4 @@ export * from "../valueSchemaToHex"; export * from "../getKeySchema"; export * from "../getValueSchema"; export * from "../getSchemaTypes"; +export * from "../getSchemaPrimitives"; diff --git a/packages/protocol-parser/src/getKeySchema.ts b/packages/protocol-parser/src/getKeySchema.ts index 1438b55374..1fe93d3d97 100644 --- a/packages/protocol-parser/src/getKeySchema.ts +++ b/packages/protocol-parser/src/getKeySchema.ts @@ -1,8 +1,10 @@ -import { Table } from "@latticexyz/config"; +import { Schema, Table } from "@latticexyz/config"; -export type getKeySchema = { - [fieldName in table["key"][number]]: table["schema"][fieldName]; -}; +export type getKeySchema
= Table extends table + ? Schema + : { + readonly [fieldName in keyof table["schema"] & table["key"][number]]: table["schema"][fieldName]; + }; export function getKeySchema
(table: table): getKeySchema
{ return Object.fromEntries(table.key.map((fieldName) => [fieldName, table.schema[fieldName]])) as getKeySchema
; diff --git a/packages/protocol-parser/src/getSchemaPrimitives.ts b/packages/protocol-parser/src/getSchemaPrimitives.ts new file mode 100644 index 0000000000..1bd1453515 --- /dev/null +++ b/packages/protocol-parser/src/getSchemaPrimitives.ts @@ -0,0 +1,6 @@ +import { Schema } from "@latticexyz/config"; +import { SchemaAbiTypeToPrimitiveType } from "@latticexyz/schema-type/internal"; + +export type getSchemaPrimitives = { + readonly [fieldName in keyof schema]: SchemaAbiTypeToPrimitiveType; +}; diff --git a/packages/protocol-parser/src/getSchemaTypes.ts b/packages/protocol-parser/src/getSchemaTypes.ts index 5f49606ccc..e44e8cf464 100644 --- a/packages/protocol-parser/src/getSchemaTypes.ts +++ b/packages/protocol-parser/src/getSchemaTypes.ts @@ -2,7 +2,7 @@ import { Schema } from "@latticexyz/config"; import { mapObject } from "@latticexyz/common/utils"; export type getSchemaTypes = { - readonly [k in keyof schema]: schema[k]["type"]; + readonly [fieldName in keyof schema]: schema[fieldName]["type"]; }; export function getSchemaTypes(schema: schema): getSchemaTypes { diff --git a/packages/protocol-parser/src/getValueSchema.ts b/packages/protocol-parser/src/getValueSchema.ts index 2a3d20afb1..543f84d972 100644 --- a/packages/protocol-parser/src/getValueSchema.ts +++ b/packages/protocol-parser/src/getValueSchema.ts @@ -1,8 +1,10 @@ -import { Table } from "@latticexyz/config"; +import { Schema, Table } from "@latticexyz/config"; -export type getValueSchema
= { - [fieldName in Exclude]: table["schema"][fieldName]; -}; +export type getValueSchema
= Table extends table + ? Schema + : { + readonly [fieldName in Exclude]: table["schema"][fieldName]; + }; export function getValueSchema
(table: table): getValueSchema
{ return Object.fromEntries( diff --git a/packages/store-indexer/src/postgres/apiRoutes.ts b/packages/store-indexer/src/postgres/apiRoutes.ts index 01a38f3e0d..749cae393f 100644 --- a/packages/store-indexer/src/postgres/apiRoutes.ts +++ b/packages/store-indexer/src/postgres/apiRoutes.ts @@ -3,7 +3,7 @@ import { Middleware } from "koa"; import Router from "@koa/router"; import compose from "koa-compose"; import { input } from "@latticexyz/store-sync/indexer-client"; -import { storeTables } from "@latticexyz/store-sync"; +import { schemasTable } from "@latticexyz/store-sync"; import { queryLogs } from "./queryLogs"; import { recordToLog } from "./recordToLog"; import { debug, error } from "../debug"; @@ -28,7 +28,7 @@ export function apiRoutes(database: Sql): Middleware { } try { - options.filters = options.filters.length > 0 ? [...options.filters, { tableId: storeTables.Tables.tableId }] : []; + options.filters = options.filters.length > 0 ? [...options.filters, { tableId: schemasTable.tableId }] : []; const records = await queryLogs(database, options ?? {}).execute(); benchmark("query records"); const logs = records.map(recordToLog); diff --git a/packages/store-indexer/src/postgres/deprecated/createQueryAdapter.ts b/packages/store-indexer/src/postgres/deprecated/createQueryAdapter.ts index 977be3e482..a3f5f76587 100644 --- a/packages/store-indexer/src/postgres/deprecated/createQueryAdapter.ts +++ b/packages/store-indexer/src/postgres/deprecated/createQueryAdapter.ts @@ -1,7 +1,7 @@ import { getAddress } from "viem"; import { PgDatabase } from "drizzle-orm/pg-core"; -import { TableWithRecords, isTableRegistrationLog, logToTable, storeTables } from "@latticexyz/store-sync"; -import { decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal"; +import { TableWithRecords, isTableRegistrationLog, logToTable, schemasTable } from "@latticexyz/store-sync"; +import { KeySchema, decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal"; import { QueryAdapter } from "@latticexyz/store-sync/trpc-indexer"; import { debug } from "../../debug"; import { getLogs } from "./getLogs"; @@ -25,19 +25,20 @@ export async function createQueryAdapter(database: PgDatabase): Promise 0 ? [...filters, { tableId: storeTables.Tables.tableId }] : [], + filters: filters.length > 0 ? [...filters, { tableId: schemasTable.tableId }] : [], }); const tables = logs.filter(isTableRegistrationLog).map(logToTable); const logsByTable = groupBy(logs, (log) => `${getAddress(log.address)}:${log.args.tableId}`); - const tablesWithRecords: TableWithRecords[] = tables.map((table) => { + const tablesWithRecords: readonly TableWithRecords[] = tables.map((table) => { const tableLogs = logsByTable.get(`${getAddress(table.address)}:${table.tableId}`) ?? []; - const records = tableLogs.map((log) => ({ - key: decodeKey(table.keySchema, log.args.keyTuple), - value: decodeValueArgs(table.valueSchema, log.args), - })); + const records = tableLogs.map((log) => { + const key = decodeKey(table.keySchema as KeySchema, log.args.keyTuple); + const value = decodeValueArgs(table.valueSchema, log.args); + return { key, value, fields: { ...key, ...value } }; + }); return { ...table, diff --git a/packages/store-indexer/src/sqlite/apiRoutes.ts b/packages/store-indexer/src/sqlite/apiRoutes.ts index 8bf13d8033..1c8e0aae6c 100644 --- a/packages/store-indexer/src/sqlite/apiRoutes.ts +++ b/packages/store-indexer/src/sqlite/apiRoutes.ts @@ -2,7 +2,7 @@ import { Middleware } from "koa"; import Router from "@koa/router"; import compose from "koa-compose"; import { input } from "@latticexyz/store-sync/indexer-client"; -import { storeTables, tablesWithRecordsToLogs } from "@latticexyz/store-sync"; +import { schemasTable, tablesWithRecordsToLogs } from "@latticexyz/store-sync"; import { debug } from "../debug"; import { createBenchmark } from "@latticexyz/common"; import { compress } from "../koa-middleware/compress"; @@ -28,7 +28,7 @@ export function apiRoutes(database: BaseSQLiteDatabase<"sync", any>): Middleware } try { - options.filters = options.filters.length > 0 ? [...options.filters, { tableId: storeTables.Tables.tableId }] : []; + options.filters = options.filters.length > 0 ? [...options.filters, { tableId: schemasTable.tableId }] : []; benchmark("parse config"); const { blockNumber, tables } = getTablesWithRecords(database, options); benchmark("query tables with records"); diff --git a/packages/store-indexer/src/sqlite/getTablesWithRecords.ts b/packages/store-indexer/src/sqlite/getTablesWithRecords.ts index a3de2112ae..eea6489ec6 100644 --- a/packages/store-indexer/src/sqlite/getTablesWithRecords.ts +++ b/packages/store-indexer/src/sqlite/getTablesWithRecords.ts @@ -3,7 +3,9 @@ import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"; import { buildTable, chainState, getTables } from "@latticexyz/store-sync/sqlite"; import { Hex, getAddress } from "viem"; import { decodeDynamicField } from "@latticexyz/protocol-parser/internal"; -import { SyncFilter, TableWithRecords } from "@latticexyz/store-sync"; +import { SyncFilter, TableRecord, TableWithRecords } from "@latticexyz/store-sync"; +import { hexToResource } from "@latticexyz/common"; +import { mapObject } from "@latticexyz/common/utils"; // TODO: refactor sqlite and replace this with getLogs to match postgres (https://github.com/latticexyz/mud/issues/1970) @@ -60,13 +62,18 @@ export function getTablesWithRecords( (filter.key1 == null || filter.key1 === keyTuple[1]), ); }); + const resource = hexToResource(table.tableId); return { ...table, - records: filteredRecords.map((record) => ({ - key: Object.fromEntries(Object.entries(table.keySchema).map(([name]) => [name, record[name]])), - value: Object.fromEntries(Object.entries(table.valueSchema).map(([name]) => [name, record[name]])), - })), - }; + type: resource.type as never, + schema: mapObject({ ...table.keySchema, ...table.valueSchema }, (type) => ({ type, internalType: type })), + key: Object.keys(table.keySchema), + records: filteredRecords.map((record): TableRecord => { + const key = Object.fromEntries(Object.entries(table.keySchema).map(([name]) => [name, record[name]])); + const value = Object.fromEntries(Object.entries(table.valueSchema).map(([name]) => [name, record[name]])); + return { key, value, fields: { ...key, ...value } }; + }), + } satisfies TableWithRecords; }); return { diff --git a/packages/store-sync/src/common.ts b/packages/store-sync/src/common.ts index 6d9c2a0eb8..c616565eba 100644 --- a/packages/store-sync/src/common.ts +++ b/packages/store-sync/src/common.ts @@ -1,19 +1,23 @@ import { Address, Block, Hex, Log, PublicClient, TransactionReceipt } from "viem"; import { StoreEventsAbiItem, StoreEventsAbi } from "@latticexyz/store"; -import { resolveConfig } from "@latticexyz/store/internal"; import { Observable } from "rxjs"; import { UnionPick } from "@latticexyz/common/type-utils"; -import { KeySchema, TableRecord, ValueSchema } from "@latticexyz/protocol-parser/internal"; +import { + getKeySchema, + getSchemaPrimitives, + getSchemaTypes, + getValueSchema, +} from "@latticexyz/protocol-parser/internal"; import storeConfig from "@latticexyz/store/mud.config"; import worldConfig from "@latticexyz/world/mud.config"; -import { flattenSchema } from "./flattenSchema"; import { Store as StoreConfig } from "@latticexyz/store"; -import { storeToV1 } from "@latticexyz/store/config/v2"; +import { Table as ConfigTable, Schema } from "@latticexyz/config"; -/** @internal Temporary workaround until we redo our config parsing and can pull this directly from the config (https://github.com/latticexyz/mud/issues/1668) */ -export const storeTables = resolveConfig(storeToV1(storeConfig)).tables; -/** @internal Temporary workaround until we redo our config parsing and can pull this directly from the config (https://github.com/latticexyz/mud/issues/1668) */ -export const worldTables = resolveConfig(storeToV1(worldConfig)).tables; +export const storeTables = storeConfig.tables; +export type storeTables = typeof storeTables; + +export const worldTables = worldConfig.tables; +export type worldTables = typeof worldTables; export const internalTableIds = [...Object.values(storeTables), ...Object.values(worldTables)].map( (table) => table.tableId, @@ -22,19 +26,21 @@ export const internalTableIds = [...Object.values(storeTables), ...Object.values export type ChainId = number; export type WorldId = `${ChainId}:${Address}`; -export type TableNamespace = string; -export type TableName = string; +export type TableRecord
= { + readonly key: getSchemaPrimitives>; + readonly value: getSchemaPrimitives>; + readonly fields: getSchemaPrimitives; +}; -export type Table = { - address: Address; - tableId: Hex; - namespace: TableNamespace; - name: TableName; - keySchema: KeySchema; - valueSchema: ValueSchema; +export type Table
= table & { + readonly address: Address; + readonly keySchema: getSchemaTypes>; + readonly valueSchema: getSchemaTypes>; }; -export type TableWithRecords = Table & { records: TableRecord[] }; +export type TableWithRecords
= Table
& { + readonly records: readonly TableRecord
[]; +}; export type StoreEventsLog = Log; export type BlockLogs = { blockNumber: StoreEventsLog["blockNumber"]; logs: readonly StoreEventsLog[] }; @@ -127,10 +133,8 @@ export type StorageAdapterLog = Partial & UnionPick Promise; -export const schemasTableId = storeTables.Tables.tableId; export const schemasTable = { - ...storeTables.Tables, - // TODO: remove once we've got everything using the new Table shape - keySchema: flattenSchema(storeTables.Tables.keySchema), - valueSchema: flattenSchema(storeTables.Tables.valueSchema), + ...storeTables.store__Tables, + keySchema: getSchemaTypes(getKeySchema(storeTables.store__Tables)), + valueSchema: getSchemaTypes(getValueSchema(storeTables.store__Tables)), }; diff --git a/packages/store-sync/src/flattenSchema.ts b/packages/store-sync/src/flattenSchema.ts deleted file mode 100644 index cba8e6b81c..0000000000 --- a/packages/store-sync/src/flattenSchema.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { mapObject } from "@latticexyz/common/utils"; -import { ValueSchema } from "@latticexyz/store/internal"; - -export function flattenSchema( - schema: schema, -): { readonly [k in keyof schema]: schema[k]["type"] } { - return mapObject(schema, (value) => value.type); -} diff --git a/packages/store-sync/src/isTableRegistrationLog.ts b/packages/store-sync/src/isTableRegistrationLog.ts index e7f9ac6acf..ac0ff9d347 100644 --- a/packages/store-sync/src/isTableRegistrationLog.ts +++ b/packages/store-sync/src/isTableRegistrationLog.ts @@ -1,4 +1,4 @@ -import { StorageAdapterLog, storeTables } from "./common"; +import { StorageAdapterLog, schemasTable } from "./common"; /** * @internal @@ -6,5 +6,5 @@ import { StorageAdapterLog, storeTables } from "./common"; export function isTableRegistrationLog( log: StorageAdapterLog, ): log is StorageAdapterLog & { eventName: "Store_SetRecord" } { - return log.eventName === "Store_SetRecord" && log.args.tableId === storeTables.Tables.tableId; + return log.eventName === "Store_SetRecord" && log.args.tableId === schemasTable.tableId; } diff --git a/packages/store-sync/src/logToTable.test.ts b/packages/store-sync/src/logToTable.test.ts index 0feb33c786..c23cf1423f 100644 --- a/packages/store-sync/src/logToTable.test.ts +++ b/packages/store-sync/src/logToTable.test.ts @@ -22,12 +22,43 @@ describe("logToTable", () => { ).toMatchInlineSnapshot(` { "address": "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c", + "key": [ + "tableId", + ], "keySchema": { "tableId": "bytes32", }, "name": "Tables", "namespace": "mudstore", + "resourceId": "0x74626d756473746f72650000000000005461626c657300000000000000000000", + "schema": { + "abiEncodedFieldNames": { + "internalType": "bytes", + "type": "bytes", + }, + "abiEncodedKeyNames": { + "internalType": "bytes", + "type": "bytes", + }, + "fieldLayout": { + "internalType": "bytes32", + "type": "bytes32", + }, + "keySchema": { + "internalType": "bytes32", + "type": "bytes32", + }, + "tableId": { + "internalType": "bytes32", + "type": "bytes32", + }, + "valueSchema": { + "internalType": "bytes32", + "type": "bytes32", + }, + }, "tableId": "0x74626d756473746f72650000000000005461626c657300000000000000000000", + "type": "table", "valueSchema": { "abiEncodedFieldNames": "bytes", "abiEncodedKeyNames": "bytes", diff --git a/packages/store-sync/src/logToTable.ts b/packages/store-sync/src/logToTable.ts index 6e9e1f5c47..9722e9bb63 100644 --- a/packages/store-sync/src/logToTable.ts +++ b/packages/store-sync/src/logToTable.ts @@ -1,7 +1,8 @@ -import { hexToSchema, decodeValue, ValueSchema } from "@latticexyz/protocol-parser/internal"; -import { Hex, concatHex, decodeAbiParameters, parseAbiParameters } from "viem"; +import { hexToSchema, decodeValue, getSchemaTypes } from "@latticexyz/protocol-parser/internal"; +import { concatHex, decodeAbiParameters, parseAbiParameters } from "viem"; import { StorageAdapterLog, Table, schemasTable } from "./common"; import { hexToResource } from "@latticexyz/common"; +import { Schema } from "@latticexyz/config"; /** * @internal @@ -12,34 +13,35 @@ export function logToTable(log: StorageAdapterLog & { eventName: "Store_SetRecor console.warn("registerSchema event is expected to have only one key in key tuple, but got multiple", log); } - const table = hexToResource(tableId); + const resource = hexToResource(tableId); const value = decodeValue( - // TODO: remove cast when we have strong types for user types - schemasTable.valueSchema as ValueSchema, + schemasTable.valueSchema, concatHex([log.args.staticData, log.args.encodedLengths, log.args.dynamicData]), ); - // TODO: remove cast when we have strong types for user types - const keySchema = hexToSchema(value.keySchema as Hex); + const solidityKeySchema = hexToSchema(value.keySchema); + const solidityValueSchema = hexToSchema(value.valueSchema); + const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedKeyNames)[0]; + const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedFieldNames)[0]; - // TODO: remove cast when we have strong types for user types - const valueSchema = hexToSchema(value.valueSchema as Hex); + const valueAbiTypes = [...solidityValueSchema.staticFields, ...solidityValueSchema.dynamicFields]; - // TODO: remove cast when we have strong types for user types - const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedKeyNames as Hex)[0]; + const keySchema = Object.fromEntries( + solidityKeySchema.staticFields.map((abiType, i) => [keyNames[i], { type: abiType, internalType: abiType }]), + ) satisfies Schema; - // TODO: remove cast when we have strong types for user types - const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedFieldNames as Hex)[0]; - - const valueAbiTypes = [...valueSchema.staticFields, ...valueSchema.dynamicFields]; + const valueSchema = Object.fromEntries( + valueAbiTypes.map((abiType, i) => [fieldNames[i], { type: abiType, internalType: abiType }]), + ) satisfies Schema; return { address: log.address, + ...resource, tableId, - namespace: table.namespace, - name: table.name, - keySchema: Object.fromEntries(keySchema.staticFields.map((abiType, i) => [keyNames[i], abiType])), - valueSchema: Object.fromEntries(valueAbiTypes.map((abiType, i) => [fieldNames[i], abiType])), - }; + schema: { ...keySchema, ...valueSchema }, + key: Object.keys(keySchema), + keySchema: getSchemaTypes(keySchema), + valueSchema: getSchemaTypes(valueSchema), + } as never; } diff --git a/packages/store-sync/src/postgres-decoded/buildTable.test.ts b/packages/store-sync/src/postgres-decoded/buildTable.test.ts index 1171b5323a..138e21d34b 100644 --- a/packages/store-sync/src/postgres-decoded/buildTable.test.ts +++ b/packages/store-sync/src/postgres-decoded/buildTable.test.ts @@ -3,11 +3,13 @@ import { buildTable } from "./buildTable"; import { getTableColumns } from "drizzle-orm"; import { getTableConfig } from "drizzle-orm/pg-core"; import { mapObject } from "@latticexyz/common/utils"; +import { resourceToHex } from "@latticexyz/common"; describe("buildTable", () => { it("should create table from schema", async () => { const table = buildTable({ address: "0xffffffffffffffffffffffffffffffffffffffff", + tableId: resourceToHex({ type: "table", namespace: "testNS", name: "UsersTable" }), namespace: "testNS", name: "UsersTable", keySchema: { x: "uint32", y: "uint32" }, @@ -68,6 +70,7 @@ describe("buildTable", () => { it("can create a singleton table", async () => { const table = buildTable({ address: "0xffffffffffffffffffffffffffffffffffffffff", + tableId: resourceToHex({ type: "table", namespace: "testNS", name: "UsersTable" }), namespace: "testNS", name: "UsersTable", keySchema: {}, diff --git a/packages/store-sync/src/postgres-decoded/buildTable.ts b/packages/store-sync/src/postgres-decoded/buildTable.ts index 115be4b6d0..9d9ac37dee 100644 --- a/packages/store-sync/src/postgres-decoded/buildTable.ts +++ b/packages/store-sync/src/postgres-decoded/buildTable.ts @@ -1,55 +1,36 @@ import { PgColumnBuilderBase, PgTableWithColumns, pgSchema } from "drizzle-orm/pg-core"; -import { Address } from "viem"; import { snakeCase } from "change-case"; -import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser/internal"; import { asBigInt, asHex } from "../postgres/columnTypes"; import { transformSchemaName } from "../postgres/transformSchemaName"; import { buildColumn } from "./buildColumn"; +import { PartialTable } from "./common"; export const metaColumns = { __keyBytes: asHex("__key_bytes").primaryKey(), __lastUpdatedBlockNumber: asBigInt("__last_updated_block_number", "numeric"), } as const satisfies Record; -type PgTableFromSchema = PgTableWithColumns<{ +type PgTableFromSchema
= PgTableWithColumns<{ dialect: "pg"; name: string; schema: string; columns: { // TODO: figure out column types // eslint-disable-next-line @typescript-eslint/no-explicit-any - [metaColumn in keyof typeof metaColumns]: any; - } & { - // TODO: figure out column types - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [keyColumn in keyof TKeySchema]: any; - } & { - // TODO: figure out column types - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [valueColumn in keyof TValueSchema]: any; + [metaColumn in keyof typeof metaColumns | keyof table["keySchema"] | keyof table["valueSchema"]]: any; }; }>; -type BuildTableOptions = { - address: Address; - namespace: string; - name: string; - keySchema: TKeySchema; - valueSchema: TValueSchema; -}; - -type BuildTableResult = PgTableFromSchema< - TKeySchema, - TValueSchema ->; +type BuildTableOptions
= table; +type BuildTableResult
= PgTableFromSchema
; -export function buildTable({ +export function buildTable
({ address, namespace, name, keySchema, valueSchema, -}: BuildTableOptions): BuildTableResult { +}: BuildTableOptions
): BuildTableResult
{ // We intentionally do not snake case the namespace due to potential conflicts // with namespaces of a similar name (e.g. `MyNamespace` vs. `my_namespace`). // TODO: consider snake case when we resolve https://github.com/latticexyz/mud/issues/1991 @@ -75,5 +56,5 @@ export function buildTable; + return table as never; } diff --git a/packages/store-sync/src/postgres-decoded/common.ts b/packages/store-sync/src/postgres-decoded/common.ts new file mode 100644 index 0000000000..a18f8d4b05 --- /dev/null +++ b/packages/store-sync/src/postgres-decoded/common.ts @@ -0,0 +1,3 @@ +import { Table } from "../common"; + +export type PartialTable = Pick; diff --git a/packages/store-sync/src/postgres-decoded/createStorageAdapter.test.ts b/packages/store-sync/src/postgres-decoded/createStorageAdapter.test.ts index 36e311b8d5..51f87ab84e 100644 --- a/packages/store-sync/src/postgres-decoded/createStorageAdapter.test.ts +++ b/packages/store-sync/src/postgres-decoded/createStorageAdapter.test.ts @@ -91,10 +91,19 @@ describe("createStorageAdapter", async () => { [ { "address": "0x7C78d585F136d7247f9deA68f60CE8A2D3F311E2", + "key": [], "keySchema": {}, "name": "NumberList", "namespace": "", + "resourceId": "0x746200000000000000000000000000004e756d6265724c697374000000000000", + "schema": { + "value": { + "internalType": "uint32[]", + "type": "uint32[]", + }, + }, "tableId": "0x746200000000000000000000000000004e756d6265724c697374000000000000", + "type": "table", "valueSchema": { "value": "uint32[]", }, diff --git a/packages/store-sync/src/postgres-decoded/createStorageAdapter.ts b/packages/store-sync/src/postgres-decoded/createStorageAdapter.ts index c89db48889..4f40357dcd 100644 --- a/packages/store-sync/src/postgres-decoded/createStorageAdapter.ts +++ b/packages/store-sync/src/postgres-decoded/createStorageAdapter.ts @@ -7,7 +7,7 @@ import { debug } from "./debug"; import { StorageAdapter, StorageAdapterBlock } from "../common"; import { isTableRegistrationLog } from "../isTableRegistrationLog"; import { logToTable } from "../logToTable"; -import { decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal"; +import { KeySchema, decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal"; import { tables as internalTables } from "../postgres/tables"; import { createStorageAdapter as createBytesStorageAdapter } from "../postgres/createStorageAdapter"; import { setupTables } from "../postgres/setupTables"; @@ -66,7 +66,7 @@ export async function createStorageAdapter, filters: { address: Hex | null; tableId: Hex | null }[] = [], -): Promise { +): Promise { const conditions = filters.map((filter) => and( filter.address != null ? eq(internalTables.recordsTable.address, filter.address) : undefined, @@ -21,7 +22,7 @@ export async function getTables( const records = await db .select() .from(internalTables.recordsTable) - .where(and(eq(internalTables.recordsTable.tableId, storeTables.Tables.tableId), or(...conditions))); + .where(and(eq(internalTables.recordsTable.tableId, schemasTable.tableId), or(...conditions))); const logs = records.map( (record) => diff --git a/packages/store-sync/src/sqlite/buildTable.test.ts b/packages/store-sync/src/sqlite/buildTable.test.ts index c68e5352d6..fda297abfd 100644 --- a/packages/store-sync/src/sqlite/buildTable.test.ts +++ b/packages/store-sync/src/sqlite/buildTable.test.ts @@ -1,10 +1,12 @@ import { describe, it, expect } from "vitest"; import { buildTable } from "./buildTable"; +import { resourceToHex } from "@latticexyz/common"; describe("buildTable", () => { it("should create table from schema", async () => { const table = buildTable({ address: "0xffffffffffffffffffffffffffffffffffffffff", + tableId: resourceToHex({ type: "table", namespace: "test", name: "users" }), namespace: "test", name: "users", keySchema: { x: "uint32", y: "uint32" }, @@ -634,6 +636,7 @@ describe("buildTable", () => { it("can create a singleton table", async () => { const table = buildTable({ address: "0xffffffffffffffffffffffffffffffffffffffff", + tableId: resourceToHex({ type: "table", namespace: "test", name: "users" }), namespace: "test", name: "users", keySchema: {}, diff --git a/packages/store-sync/src/sqlite/buildTable.ts b/packages/store-sync/src/sqlite/buildTable.ts index eaf85b0fca..367221e7c7 100644 --- a/packages/store-sync/src/sqlite/buildTable.ts +++ b/packages/store-sync/src/sqlite/buildTable.ts @@ -1,9 +1,8 @@ import { SQLiteColumnBuilderBase, SQLiteTableWithColumns, sqliteTable } from "drizzle-orm/sqlite-core"; import { buildColumn } from "./buildColumn"; -import { Address } from "viem"; import { getTableName } from "./getTableName"; -import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser/internal"; import { snakeCase } from "change-case"; +import { PartialTable } from "./common"; export const metaColumns = { __key: buildColumn("__key", "bytes").primaryKey(), @@ -15,45 +14,27 @@ export const metaColumns = { __isDeleted: buildColumn("__isDeleted", "bool").notNull(), } as const satisfies Record; -type SQLiteTableFromSchema = SQLiteTableWithColumns<{ +type SQLiteTableFromSchema
= SQLiteTableWithColumns<{ dialect: "sqlite"; name: string; schema: string | undefined; columns: { // TODO: figure out column types // eslint-disable-next-line @typescript-eslint/no-explicit-any - [metaColumn in keyof typeof metaColumns]: any; - } & { - // TODO: figure out column types - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [keyColumn in keyof TKeySchema]: any; - } & { - // TODO: figure out column types - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [valueColumn in keyof TValueSchema]: any; + [metaColumn in keyof typeof metaColumns | keyof table["keySchema"] | keyof table["valueSchema"]]: any; }; }>; -type CreateSqliteTableOptions = { - address: Address; - namespace: string; - name: string; - keySchema: TKeySchema; - valueSchema: TValueSchema; -}; - -type CreateSqliteTableResult = SQLiteTableFromSchema< - TKeySchema, - TValueSchema ->; +type BuildTableOptions
= table; +type BuildTableResult
= SQLiteTableFromSchema
; -export function buildTable({ +export function buildTable
({ address, namespace, name, keySchema, valueSchema, -}: CreateSqliteTableOptions): CreateSqliteTableResult { +}: BuildTableOptions
): BuildTableResult
{ const tableName = getTableName(address, namespace, name); const keyColumns = Object.fromEntries( @@ -77,5 +58,5 @@ export function buildTable; + return table as never; } diff --git a/packages/store-sync/src/sqlite/common.ts b/packages/store-sync/src/sqlite/common.ts new file mode 100644 index 0000000000..a18f8d4b05 --- /dev/null +++ b/packages/store-sync/src/sqlite/common.ts @@ -0,0 +1,3 @@ +import { Table } from "../common"; + +export type PartialTable = Pick; diff --git a/packages/store-sync/src/sqlite/getTables.ts b/packages/store-sync/src/sqlite/getTables.ts index 01f9e90aa9..9228f4e631 100644 --- a/packages/store-sync/src/sqlite/getTables.ts +++ b/packages/store-sync/src/sqlite/getTables.ts @@ -1,13 +1,13 @@ import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"; import { asc, inArray } from "drizzle-orm"; -import { Table } from "../common"; import { getTableName } from "./getTableName"; import { mudStoreTables } from "./internalTables"; +import { PartialTable } from "./common"; export function getTables( db: BaseSQLiteDatabase<"sync", void>, - conditions: Pick[] = [], -): Table[] { + conditions: Pick[] = [], +): PartialTable[] { const ids = Array.from( new Set(conditions.map((condition) => getTableName(condition.address, condition.namespace, condition.name))), ); diff --git a/packages/store-sync/src/sqlite/internalTables.ts b/packages/store-sync/src/sqlite/internalTables.ts index 97c826eead..6765e05646 100644 --- a/packages/store-sync/src/sqlite/internalTables.ts +++ b/packages/store-sync/src/sqlite/internalTables.ts @@ -1,6 +1,6 @@ import { blob, integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; import { address, asHex, json } from "./columnTypes"; -import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser/internal"; +import { PartialTable } from "./common"; export const chainState = sqliteTable("__chainState", { schemaVersion: integer("schema_version").notNull().primaryKey(), @@ -17,8 +17,8 @@ export const mudStoreTables = sqliteTable("__mudStoreTables", { tableId: asHex("table_id").notNull(), namespace: text("namespace").notNull(), name: text("name").notNull(), - keySchema: json("key_schema").notNull(), - valueSchema: json("value_schema").notNull(), + keySchema: json("key_schema").notNull(), + valueSchema: json("value_schema").notNull(), lastUpdatedBlockNumber: blob("last_updated_block_number", { mode: "bigint" }), // TODO: last block hash? lastError: text("last_error"), diff --git a/packages/store-sync/src/sqlite/sqliteStorage.ts b/packages/store-sync/src/sqlite/sqliteStorage.ts index 4dcc7a216b..cb77704c3e 100644 --- a/packages/store-sync/src/sqlite/sqliteStorage.ts +++ b/packages/store-sync/src/sqlite/sqliteStorage.ts @@ -13,7 +13,7 @@ import { StorageAdapter } from "../common"; import { isTableRegistrationLog } from "../isTableRegistrationLog"; import { logToTable } from "../logToTable"; import { hexToResource, resourceToLabel, spliceHex } from "@latticexyz/common"; -import { decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal"; +import { KeySchema, decodeKey, decodeValueArgs } from "@latticexyz/protocol-parser/internal"; // TODO: upgrade drizzle and use async sqlite interface for consistency @@ -46,7 +46,12 @@ export async function sqliteStorage({ .values({ schemaVersion, id: getTableName(table.address, table.namespace, table.name), - ...table, + address: table.address, + tableId: table.tableId, + namespace: table.namespace, + name: table.name, + keySchema: table.keySchema, + valueSchema: table.valueSchema, lastUpdatedBlockNumber: blockNumber, }) .onConflictDoNothing() @@ -97,7 +102,7 @@ export async function sqliteStorage({ const sqlTable = buildTable(table); const uniqueKey = concatHex(log.args.keyTuple as Hex[]); - const key = decodeKey(table.keySchema, log.args.keyTuple); + const key = decodeKey(table.keySchema as KeySchema, log.args.keyTuple); if (log.eventName === "Store_SetRecord") { const value = decodeValueArgs(table.valueSchema, log.args); diff --git a/packages/store-sync/src/tableToLog.test.ts b/packages/store-sync/src/tableToLog.test.ts index ca1f9de5f5..541b2e522b 100644 --- a/packages/store-sync/src/tableToLog.test.ts +++ b/packages/store-sync/src/tableToLog.test.ts @@ -1,19 +1,14 @@ /* eslint-disable max-len */ import { describe, it, expect } from "vitest"; import { tableToLog } from "./tableToLog"; -import { storeTables } from "./common"; -import { flattenSchema } from "./flattenSchema"; +import { schemasTable } from "./common"; describe("tableToLog", () => { it("should convert a table object to table registration log", async () => { expect( tableToLog({ + ...schemasTable, address: "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c", - tableId: storeTables.Tables.tableId, - namespace: storeTables.Tables.namespace, - name: storeTables.Tables.name, - keySchema: flattenSchema(storeTables.Tables.keySchema), - valueSchema: flattenSchema(storeTables.Tables.valueSchema), }), ).toMatchInlineSnapshot(` { diff --git a/packages/store-sync/src/tableToLog.ts b/packages/store-sync/src/tableToLog.ts index adadbdfb0f..78fd24a320 100644 --- a/packages/store-sync/src/tableToLog.ts +++ b/packages/store-sync/src/tableToLog.ts @@ -6,8 +6,7 @@ import { valueSchemaToHex, } from "@latticexyz/protocol-parser/internal"; import { encodeAbiParameters, parseAbiParameters } from "viem"; -import { StorageAdapterLog, Table, storeTables } from "./common"; -import { flattenSchema } from "./flattenSchema"; +import { StorageAdapterLog, Table, schemasTable } from "./common"; /** * @internal @@ -17,11 +16,11 @@ export function tableToLog(table: Table): StorageAdapterLog & { eventName: "Stor eventName: "Store_SetRecord", address: table.address, args: { - tableId: storeTables.Tables.tableId, - keyTuple: encodeKey(flattenSchema(storeTables.Tables.keySchema), { tableId: table.tableId }), - ...encodeValueArgs(flattenSchema(storeTables.Tables.valueSchema), { + tableId: schemasTable.tableId, + keyTuple: encodeKey(schemasTable.keySchema, { tableId: table.tableId }), + ...encodeValueArgs(schemasTable.valueSchema, { fieldLayout: valueSchemaToFieldLayoutHex(table.valueSchema), - keySchema: keySchemaToHex(table.keySchema), + keySchema: keySchemaToHex(table.keySchema as never), valueSchema: valueSchemaToHex(table.valueSchema), abiEncodedKeyNames: encodeAbiParameters(parseAbiParameters("string[]"), [Object.keys(table.keySchema)]), abiEncodedFieldNames: encodeAbiParameters(parseAbiParameters("string[]"), [Object.keys(table.valueSchema)]), diff --git a/packages/store-sync/src/tablesWithRecordsToLogs.ts b/packages/store-sync/src/tablesWithRecordsToLogs.ts index 0e8dc6f07a..789474ddaa 100644 --- a/packages/store-sync/src/tablesWithRecordsToLogs.ts +++ b/packages/store-sync/src/tablesWithRecordsToLogs.ts @@ -1,5 +1,11 @@ import { StorageAdapterLog, TableWithRecords } from "./common"; -import { encodeKey, encodeValueArgs } from "@latticexyz/protocol-parser/internal"; +import { + encodeKey, + encodeValueArgs, + getKeySchema, + getSchemaTypes, + getValueSchema, +} from "@latticexyz/protocol-parser/internal"; import { tableToLog } from "./tableToLog"; /** @@ -15,8 +21,9 @@ export function tablesWithRecordsToLogs(tables: readonly TableWithRecords[]): St address: table.address, args: { tableId: table.tableId, - keyTuple: encodeKey(table.keySchema, record.key), - ...encodeValueArgs(table.valueSchema, record.value), + // TODO: migrate away from these helpers + keyTuple: encodeKey(getSchemaTypes(getKeySchema(table)) as never, record.key as never), + ...encodeValueArgs(getSchemaTypes(getValueSchema(table)), record.value), }, }), ), diff --git a/packages/store-sync/src/trpc-indexer/common.ts b/packages/store-sync/src/trpc-indexer/common.ts index 63bcd22ec3..7e5334aead 100644 --- a/packages/store-sync/src/trpc-indexer/common.ts +++ b/packages/store-sync/src/trpc-indexer/common.ts @@ -6,8 +6,8 @@ export type QueryAdapter = { * @deprecated */ findAll: (opts: { chainId: number; address?: Hex; filters?: readonly SyncFilter[] }) => Promise<{ - blockNumber: bigint | null; - tables: readonly TableWithRecords[]; + readonly blockNumber: bigint | null; + readonly tables: readonly TableWithRecords[]; }>; getLogs: (opts: { readonly chainId: number; diff --git a/packages/store-sync/src/zustand/common.ts b/packages/store-sync/src/zustand/common.ts index 5f5a64edc4..738efea940 100644 --- a/packages/store-sync/src/zustand/common.ts +++ b/packages/store-sync/src/zustand/common.ts @@ -1,5 +1,5 @@ import { Table } from "@latticexyz/config"; -import { SchemaToPrimitives, getKeySchema, getSchemaTypes, getValueSchema } from "@latticexyz/protocol-parser/internal"; +import { getKeySchema, getSchemaPrimitives, getValueSchema } from "@latticexyz/protocol-parser/internal"; import { Hex } from "viem"; export type RawRecord = { @@ -17,9 +17,9 @@ export type TableRecord
= { readonly id: string; readonly table: table; readonly keyTuple: readonly Hex[]; - readonly key: SchemaToPrimitives>>; - readonly value: SchemaToPrimitives>>; - readonly fields: SchemaToPrimitives>; + readonly key: getSchemaPrimitives>; + readonly value: getSchemaPrimitives>; + readonly fields: getSchemaPrimitives; }; export type { Table }; diff --git a/packages/store-sync/src/zustand/createStorageAdapter.test.ts b/packages/store-sync/src/zustand/createStorageAdapter.test.ts index 63cf544712..16b8a6189d 100644 --- a/packages/store-sync/src/zustand/createStorageAdapter.test.ts +++ b/packages/store-sync/src/zustand/createStorageAdapter.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from "vitest"; import { storeEventsAbi } from "@latticexyz/store"; import { createStorageAdapter } from "./createStorageAdapter"; import { createStore } from "./createStore"; -import { configV2 as config, deployMockGame } from "../../test/mockGame"; +import { config, deployMockGame } from "../../test/mockGame"; import { fetchAndStoreLogs } from "../fetchAndStoreLogs"; import { testClient } from "../../test/common"; import { getBlockNumber } from "viem/actions"; diff --git a/packages/store-sync/test/mockGame.ts b/packages/store-sync/test/mockGame.ts index 29e2ca0fab..3cc49bbb44 100644 --- a/packages/store-sync/test/mockGame.ts +++ b/packages/store-sync/test/mockGame.ts @@ -1,14 +1,10 @@ import { execa } from "execa"; import { anvilRpcUrl } from "./common"; -import configV2 from "mock-game-contracts/mud.config"; -import { resolveConfig } from "@latticexyz/store/internal"; import { Hex, isHex } from "viem"; +import config from "mock-game-contracts/mud.config"; import worldAbi from "mock-game-contracts/out/IWorld.sol/IWorld.abi.json"; -import { storeToV1 } from "@latticexyz/store/config/v2"; -export { configV2 }; -export const config = resolveConfig(storeToV1(configV2)); -export { worldAbi }; +export { config, worldAbi }; export async function deployMockGame(): Promise { console.log("deploying mock game to", anvilRpcUrl); diff --git a/packages/store-sync/test/utils.ts b/packages/store-sync/test/utils.ts index b9c002f620..becbcb47e6 100644 --- a/packages/store-sync/test/utils.ts +++ b/packages/store-sync/test/utils.ts @@ -4,25 +4,28 @@ import { foundry } from "viem/chains"; import initSqlJs from "sql.js"; import mudConfig from "../../../e2e/packages/contracts/mud.config"; import { createWorld } from "@latticexyz/recs"; -import { resolveConfig } from "@latticexyz/store/internal"; -import { RecsStorageAdapter, recsStorage } from "../src/recs"; +import { + CreateStorageAdapterResult as CreateRecsStorageAdapterResult, + createStorageAdapter as createRecsStorageAdapter, +} from "../src/recs"; import { sqliteStorage } from "../src/sqlite"; -import { ZustandStore, createStorageAdapter, createStore } from "../src/zustand"; +import { ZustandStore, createStorageAdapter as createZustandStorageAdapter, createStore } from "../src/zustand"; import { StorageAdapter } from "../src"; -export const tables = resolveConfig(mudConfig).tables; +export const tables = mudConfig.tables; +export type tables = typeof tables; -export function createRecsStorage(): RecsStorageAdapter { - return recsStorage({ world: createWorld(), tables }); +export function createRecsStorage(): CreateRecsStorageAdapterResult { + return createRecsStorageAdapter({ world: createWorld(), tables }); } export function createZustandStorage(): { - useStore: ZustandStore; + useStore: ZustandStore; storageAdapter: StorageAdapter; } { const useStore = createStore({ tables }); - return { useStore, storageAdapter: createStorageAdapter({ store: useStore }) }; + return { useStore, storageAdapter: createZustandStorageAdapter({ store: useStore }) }; } export async function createSqliteStorage(): Promise<{ diff --git a/tsconfig.paths.json b/tsconfig.paths.json index 10fd1ec34f..70a8f9eb05 100644 --- a/tsconfig.paths.json +++ b/tsconfig.paths.json @@ -35,6 +35,7 @@ "@latticexyz/store/register": ["./packages/store/ts/register/index.ts"], "@latticexyz/store/out/*": ["./packages/store/out/*"], "@latticexyz/store/*": ["./packages/store/ts/exports/*.ts"], + "@latticexyz/store-sync": ["./packages/store-sync/src/index.ts"], "@latticexyz/store-sync/*": ["./packages/store-sync/src/*"], "@latticexyz/utils": ["./packages/utils/src/index.ts"], "@latticexyz/world": ["./packages/world/ts/exports/index.ts"],