Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(network): integrate initial sync from MODE #493

Merged
merged 123 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
93aea58
feat: stand-alone v1 migrator script with opcraft data as case study
Feb 13, 2023
ed45102
feat: wip mode with query layer for per-table + full state fetch + count
Feb 15, 2023
3a83e1c
feat: super wip new query response table-like works exactly as i want…
Feb 16, 2023
c9fecb1
feat: misc
Feb 16, 2023
1734c8d
feat: add back projections
Feb 16, 2023
120e343
feat: restructure + redesign mode query layer package
Feb 17, 2023
2702ea2
feat: findAll() with new serialization format
Feb 17, 2023
501faa8
feat: fix migrator
Feb 22, 2023
445d974
feat: join() and correct projection support for all endpoints
Feb 22, 2023
8348c90
feat: streaming for all tables for insert/update/delete
Feb 23, 2023
89b80d4
feat: ingress into mode
Mar 9, 2023
a55ee3b
feat: namespacing + table renaming + adjustments for new storecore ta…
Mar 14, 2023
bfb52e5
feat: stuff
Mar 15, 2023
cce5aeb
feat: remove v1 data references + schema mapping in favor of schema c…
Mar 15, 2023
b4cf0f8
feat: refactor tablename/tableid stuff to standardize
Mar 15, 2023
de83573
feat: store table id for completeness
Mar 15, 2023
78a7ecf
feat: prevent updates to internal tables from getting streamed on the…
Mar 15, 2023
1b315cd
feat: fix bug that would panic if the bytes being encoded were zero +…
Mar 15, 2023
94f9053
feat: logging format
Mar 15, 2023
0496dba
feat: use storecore table names
Mar 16, 2023
77e17b1
feat: capitalized col names for compatibility with client
Mar 16, 2023
f3797d5
feat: changes to find() to work with new schema / namespace layout
Mar 16, 2023
48bf469
feat: schema validation on join endpoint
Mar 16, 2023
e0b77bc
feat: organize ops into packages
Mar 16, 2023
daf87b2
feat: move config into package
Mar 16, 2023
b1e665b
feat: rename function for clarity
Mar 16, 2023
34bd3af
feat: more refined querying on schema table
Mar 16, 2023
5ff944a
feat: read layer + handlers for setField update/insert row
Mar 16, 2023
0222ba1
feat: move block_number table under chain specific namespace
Mar 16, 2023
97cfd4c
feat: include chain + worldAddress namespaced tables in findAll()
Mar 16, 2023
eb30ccd
feat(network): wip mode integration
alvrs Mar 15, 2023
cb56900
feat(network): add new deployment address
alvrs Mar 15, 2023
72b6d82
chore(network): update world address
alvrs Mar 15, 2023
c4076e4
refactor(schema-type): SchemaTypeId -> SchemaTypeToAbiType
holic Mar 16, 2023
72d0a0e
feat(utils): TableId.fromHexString
holic Mar 16, 2023
9431421
feat(schema-type): add reverse mapping for abi type to schema type
holic Mar 16, 2023
40d2e99
feat(network): sync data from mode
holic Mar 16, 2023
ad01b4b
feat(network): parse new table names
holic Mar 16, 2023
b582781
feat(network): register schema/metadata from mode
holic Mar 16, 2023
e6905eb
fix(network): await registration promises
holic Mar 16, 2023
bdaa1f5
feat(network): add block number, update with latest mode changes
holic Mar 16, 2023
bc41554
fix(network): refactor/fix v2 uint/int decoding
holic Mar 17, 2023
432d6ed
feat: proper key handling / decoding
Mar 21, 2023
63d5e2b
feat: add schema type parsing based on schematype ranges
Mar 21, 2023
0efa0ad
chore: rename the col name casing map in schema
Mar 21, 2023
7ec4927
feat: add flexible sync + buffering logic
Mar 21, 2023
47e6a1f
feat: namespace changes to filter schemas + state response endpoint n…
Mar 21, 2023
a603335
feat: streaming all chain / table events + separation into inserted/u…
Mar 21, 2023
a63ae3e
feat: add internal sync status table
Mar 21, 2023
5111594
feat: getstate for single table + streambuilder to filter streamed ta…
Mar 22, 2023
de5d7d5
chore: clean up service definition
Mar 22, 2023
4ade99a
feat: proper streaming namespace filtering
Mar 22, 2023
3bddb09
docs: function comments
Mar 23, 2023
6021138
Merge remote-tracking branch 'origin/authcall/v2-mode-mvp' into mode-…
holic Mar 27, 2023
d4cd5db
feat(network): update to latest mode
holic Mar 27, 2023
912d778
docs(network): no longer an issue after decoding fixes
holic Mar 27, 2023
8ee050f
feat: stand-alone v1 migrator script with opcraft data as case study
Feb 13, 2023
49127f7
feat: wip mode with query layer for per-table + full state fetch + count
Feb 15, 2023
9253777
feat: super wip new query response table-like works exactly as i want…
Feb 16, 2023
2fd1940
feat: misc
Feb 16, 2023
9d5789a
feat: add back projections
Feb 16, 2023
968e5a3
feat: restructure + redesign mode query layer package
Feb 17, 2023
24c8595
feat: findAll() with new serialization format
Feb 17, 2023
e277a40
feat: fix migrator
Feb 22, 2023
29eb377
feat: join() and correct projection support for all endpoints
Feb 22, 2023
745e523
feat: streaming for all tables for insert/update/delete
Feb 23, 2023
3f91607
feat: ingress into mode
Mar 9, 2023
e7600a3
feat: namespacing + table renaming + adjustments for new storecore ta…
Mar 14, 2023
8eb3d07
feat: stuff
Mar 15, 2023
62bef0d
feat: remove v1 data references + schema mapping in favor of schema c…
Mar 15, 2023
0287083
feat: refactor tablename/tableid stuff to standardize
Mar 15, 2023
7993882
feat: store table id for completeness
Mar 15, 2023
a976ee1
feat: prevent updates to internal tables from getting streamed on the…
Mar 15, 2023
0f4b828
feat: fix bug that would panic if the bytes being encoded were zero +…
Mar 15, 2023
2c7319a
feat: logging format
Mar 15, 2023
a5402f5
feat: use storecore table names
Mar 16, 2023
904c98c
feat: capitalized col names for compatibility with client
Mar 16, 2023
84182fd
feat: changes to find() to work with new schema / namespace layout
Mar 16, 2023
d37e961
feat: schema validation on join endpoint
Mar 16, 2023
9426d9a
feat: organize ops into packages
Mar 16, 2023
0c4a533
feat: move config into package
Mar 16, 2023
83c26d4
feat: rename function for clarity
Mar 16, 2023
dd22073
feat: more refined querying on schema table
Mar 16, 2023
4081916
feat: read layer + handlers for setField update/insert row
Mar 16, 2023
a55d551
feat: move block_number table under chain specific namespace
Mar 16, 2023
656fe45
feat: include chain + worldAddress namespaced tables in findAll()
Mar 16, 2023
8c04ac3
feat: proper key handling / decoding
Mar 21, 2023
a20e7b6
feat: add schema type parsing based on schematype ranges
Mar 21, 2023
812c598
chore: rename the col name casing map in schema
Mar 21, 2023
2b4a2ea
feat: add flexible sync + buffering logic
Mar 21, 2023
5cc26cb
feat: namespace changes to filter schemas + state response endpoint n…
Mar 21, 2023
6fdba32
feat: streaming all chain / table events + separation into inserted/u…
Mar 21, 2023
1f2af98
feat: add internal sync status table
Mar 21, 2023
a1d13eb
feat: getstate for single table + streambuilder to filter streamed ta…
Mar 22, 2023
4f0eec9
chore: clean up service definition
Mar 22, 2023
853744d
feat: proper streaming namespace filtering
Mar 22, 2023
8c9e9e7
docs: function comments
Mar 23, 2023
77ffed5
fix: proper single state validation
Mar 27, 2023
9055df6
feat: add key and value bytes32 schemas to internal schemas table
Mar 27, 2023
724dcb6
docs: updated readme
Mar 27, 2023
2f89c4a
feat: write schema and metadata rows into mudstore tables
Mar 29, 2023
4a86741
chore: remove console logs
Mar 30, 2023
67726dd
Merge branch 'authcall/v2-mode-mvp' into mode-client
alvrs Mar 30, 2023
d3ef1fc
chore: update comment
alvrs Mar 30, 2023
11c655a
refactor: update get mode block number
alvrs Mar 30, 2023
b9468cd
feat: add type to decodeValue
alvrs Mar 30, 2023
f2fbb8f
chore: better log
alvrs Mar 30, 2023
95d900e
fix: update schema table schema
alvrs Mar 30, 2023
76781fa
feat: add sanity check to getBlockNumberFromModeTable
alvrs Mar 30, 2023
47b95d2
chore: remove console log
alvrs Mar 30, 2023
3da4567
chore: clean up
alvrs Mar 30, 2023
d788381
chore: remove comment
alvrs Mar 30, 2023
c4fd692
feat: fix up SyncWorker
alvrs Mar 30, 2023
a2225be
refactor: rename world to store in fetchStoreEvents
alvrs Mar 31, 2023
666149a
fix: handle invalid mode field type
alvrs Mar 31, 2023
9045daa
chore: link gh issue to track MODE issue
alvrs Mar 31, 2023
da10fad
Merge branch 'main' into mode-client
alvrs Mar 31, 2023
bea1753
chore(network): remove debug log
alvrs Mar 31, 2023
3345fdf
fix(network): use abi decoder to decode values from MODE
alvrs Mar 31, 2023
e932190
build: move viem to peer dependencies
alvrs Mar 31, 2023
ebc3cc6
refactor(network): use arrayToHex until viem refactor
alvrs Mar 31, 2023
0096a71
test(network): fix test setup for TextEncoder
alvrs Mar 31, 2023
dd62ef6
test(std-client): update type test
alvrs Mar 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/network/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export default {
preset: "ts-jest",
roots: ["src"],
testEnvironment: "jsdom",
setupFilesAfterEnv: ["./jest.setup.ts"],
moduleNameMapper: {
// fix TS build issues
"^(..?/.*).js$": "$1",
Expand Down
7 changes: 7 additions & 0 deletions packages/network/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TextEncoder } from "util";

// TextEncoder APIs are used by viem, but are not provided by
// jsdom, all node versions supported provide these via the util module
if (typeof globalThis.TextEncoder === "undefined") {
globalThis.TextEncoder = TextEncoder;
}
6 changes: 4 additions & 2 deletions packages/network/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"nice-grpc-web": "^2.0.0",
"observable-webworker": "^4.0.1",
"rxjs": "^7.5.5",
"threads": "^1.7.0"
"threads": "^1.7.0",
"viem": "^0.1.25"
},
"devDependencies": {
"@ethersproject/abi": "^5.6.0",
Expand Down Expand Up @@ -77,7 +78,8 @@
"tslib": "^2.3.1",
"typedoc": "0.23.21",
"typedoc-plugin-markdown": "^3.13.6",
"typescript": "^4.9.5"
"typescript": "^4.9.5",
"viem": "^0.1.25"
},
"gitHead": "914a1e0ae4a573d685841ca2ea921435057deb8f"
}
1 change: 1 addition & 0 deletions packages/network/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export type SyncWorkerConfig = {
worldContract: ContractConfig;
disableCache?: boolean;
chainId: number;
modeUrl?: string;
snapshotServiceUrl?: string;
streamServiceUrl?: string;
fetchSystemCalls?: boolean;
Expand Down
8 changes: 4 additions & 4 deletions packages/network/src/v2/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ export const metadataTableId = new TableId("mudstore", "StoreMetadata");

export const storeEvents = ["StoreSetRecord", "StoreSetField", "StoreDeleteRecord"] as const;

export type TableSchema = {
export type TableSchema = Readonly<{
staticDataLength: number;
staticFields: SchemaType[];
dynamicFields: SchemaType[];
rawSchema: string;
abi: string;
isEmpty: boolean;
};
}>;

export type TableMetadata = {
export type TableMetadata = Readonly<{
tableName: string;
fieldNames: string[];
};
}>;
10 changes: 5 additions & 5 deletions packages/network/src/v2/fetchStoreEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import { storeEvents } from "./common";
import { ecsEventFromLog } from "./ecsEventFromLog";

export async function fetchStoreEvents(
contract: Contract,
store: Contract,
fromBlock: number,
toBlock: number
): Promise<NetworkComponentUpdate[]> {
const topicSets = storeEvents.map((eventName) => contract.filters[eventName]().topics).filter(isDefined);
const topicSets = storeEvents.map((eventName) => store.filters[eventName]().topics).filter(isDefined);

const logSets = await Promise.all(
topicSets.map((topics) => contract.provider.getLogs({ address: contract.address, topics, fromBlock, toBlock }))
topicSets.map((topics) => store.provider.getLogs({ address: store.address, topics, fromBlock, toBlock }))
);

const logs = orderBy(
logSets.flatMap((logs) => logs.map((log) => ({ log, parsedLog: contract.interface.parseLog(log) }))),
logSets.flatMap((logs) => logs.map((log) => ({ log, parsedLog: store.interface.parseLog(log) }))),
["log.blockNumber", "log.logIndex"]
);

Expand All @@ -29,7 +29,7 @@ export async function fetchStoreEvents(
const ecsEvents = await Promise.all(
logs.map(({ log, parsedLog }) => {
const { transactionHash, logIndex } = log;
return ecsEventFromLog(contract, log, parsedLog, lastLogForTx[transactionHash] === logIndex);
return ecsEventFromLog(store, log, parsedLog, lastLogForTx[transactionHash] === logIndex);
})
);

Expand Down
11 changes: 11 additions & 0 deletions packages/network/src/v2/mode/createModeClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { QueryLayerClient, QueryLayerDefinition } from "@latticexyz/services/protobuf/ts/mode/mode";
import { createChannel, createClient } from "nice-grpc-web";

/**
* Create a MODE QueryLayerClient
* @param url MUDE URL
* @returns MODE QueryLayerClient
*/
export function createModeClient(url: string): QueryLayerClient {
return createClient(QueryLayerDefinition, createChannel(url));
}
8 changes: 8 additions & 0 deletions packages/network/src/v2/mode/getBlockNumberFromModeTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { GenericTable } from "@latticexyz/services/protobuf/ts/mode/mode";
import { SchemaType } from "@latticexyz/schema-type";
import { decodeValue } from "../schemas/decodeValue";

export function getBlockNumberFromModeTable(table: GenericTable): number {
if (table.cols[0] !== "block_number") throw new Error("Table does not contain block_number column");
return Number(decodeValue(SchemaType.UINT256, table.rows[0].values[0]));
}
13 changes: 13 additions & 0 deletions packages/network/src/v2/mode/getModeBlockNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { QueryLayerClient } from "@latticexyz/services/protobuf/ts/mode/mode";
import { getBlockNumberFromModeTable } from "./getBlockNumberFromModeTable";

export async function getModeBlockNumber(client: QueryLayerClient, chainId: number): Promise<number> {
const response = await client.single__GetState({
table: "block_number",
namespace: {
chainId: chainId.toString(),
},
});
const blockNumber = getBlockNumberFromModeTable(response.chainTables["block_number"]);
return blockNumber;
}
84 changes: 84 additions & 0 deletions packages/network/src/v2/mode/syncTablesFromMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { ComponentValue } from "@latticexyz/recs";
import { AbiTypeToSchemaType, encodeSchema } from "@latticexyz/schema-type";
import { QueryLayerClient } from "@latticexyz/services/protobuf/ts/mode/mode";
import { arrayToHex, TableId } from "@latticexyz/utils";
import { Contract } from "ethers";
import { NetworkEvents } from "../../types";

import { CacheStore, createCacheStore, storeEvent } from "../../workers";
import { keyTupleToEntityID } from "../keyTupleToEntityID";
import { registerMetadata } from "../schemas/tableMetadata";
import { registerSchema } from "../schemas/tableSchemas";
import { getBlockNumberFromModeTable } from "./getBlockNumberFromModeTable";
import { decodeAbiParameters } from "viem";

export async function syncTablesFromMode(
client: QueryLayerClient,
chainId: number,
world: Contract,
setPercentage?: (progress: number) => void
): Promise<CacheStore> {
const cacheStore = createCacheStore();

const response = await client.getState({
chainTables: [],
worldTables: [],
namespace: {
chainId: chainId.toString(),
worldAddress: world.address,
},
});
console.log("syncTablesFromMode", response);

const numRowsTotal = Object.values(response.worldTables).reduce((sum, table) => sum + table.rows.length, 0);
let numRowsProcessed = 0;

const blockNumber = getBlockNumberFromModeTable(response.chainTables["block_number"]);
const registrationPromises: Promise<unknown>[] = [];

for (const [fullTableName, { rows, cols, types }] of Object.entries(response.worldTables)) {
const [tableNamespace, tableName] = fullTableName.split("__");
const tableId = new TableId(tableNamespace, tableName);

const component = tableId.toString();

// TODO: separate keys and values/fields in MODE, but we'll infer for now
const keyLength = cols.findIndex((col) => !col.startsWith("key_"));
const keyAbiTypes = types.slice(0, keyLength);
const fieldNames = cols.slice(keyLength);
// TODO: remove this hack once MODE is fixed (https://github.com/latticexyz/mud/issues/444)
const fieldAbiTypes = types.slice(keyLength).map((modeType) => modeType.match(/tuple\((.*)\[]\)/)?.[1] ?? modeType);
const fieldSchemaTypes = fieldAbiTypes.map((abiType) => AbiTypeToSchemaType[abiType]);

const rawSchema = arrayToHex(encodeSchema(fieldSchemaTypes));
// TODO: refactor registerSchema/registerMetadata to take chain+world address rather than Contract
registrationPromises.push(registerSchema(world, tableId, rawSchema));
registrationPromises.push(registerMetadata(world, tableId, { tableName, fieldNames }));

for (const row of rows) {
console.log(tableName, keyAbiTypes, fieldAbiTypes, row.values);
const keyTuple = row.values
.slice(0, keyLength)
.map((bytes, i) => decodeAbiParameters([{ type: keyAbiTypes[i] }], arrayToHex(bytes)));
const values = row.values
.slice(keyLength)
.map((bytes, i) => decodeAbiParameters([{ type: fieldAbiTypes[i] }], arrayToHex(bytes)));

const entity = keyTupleToEntityID(keyTuple);
const value = Object.fromEntries(values.map((value, i) => [fieldNames[i], value])) as ComponentValue;

storeEvent(cacheStore, { type: NetworkEvents.NetworkComponentUpdate, component, entity, value, blockNumber });

numRowsProcessed++;
// Update progress every 100 rows
if (numRowsProcessed % 100 === 0 && setPercentage) {
setPercentage(Math.floor(numRowsProcessed / numRowsTotal));
}
}
}

// make sure all schemas/metadata are registered before returning to avoid downstream lookup issues
await Promise.all(registrationPromises);

return cacheStore;
}
6 changes: 3 additions & 3 deletions packages/network/src/v2/schemas/decodeValue.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SchemaType } from "@latticexyz/schema-type";
import { SchemaType, SchemaTypeToPrimitive } from "@latticexyz/schema-type";
import { decodeDynamicField } from "./decodeDynamicField";
import { decodeStaticField } from "./decodeStaticField";

Expand Down Expand Up @@ -103,8 +103,8 @@ export function decodeValue<T extends SchemaType>(schemaType: T, bytes: Uint8Arr
case SchemaType.BYTES31:
case SchemaType.BYTES32:
case SchemaType.ADDRESS:
return decodeStaticField(schemaType, bytes, 0);
return decodeStaticField(schemaType, bytes, 0) as SchemaTypeToPrimitive[T];
default:
return decodeDynamicField(schemaType, bytes);
return decodeDynamicField(schemaType, bytes) as SchemaTypeToPrimitive[T];
}
}
9 changes: 8 additions & 1 deletion packages/network/src/v2/schemas/tableMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { arrayToHex, TableId } from "@latticexyz/utils";
import { TableId } from "@latticexyz/utils";
import { Contract, utils } from "ethers";
import { metadataTableId, schemaTableId, TableMetadata } from "../common";
import { decodeData } from "./decodeData";
Expand Down Expand Up @@ -68,6 +68,13 @@ export function registerMetadata(
}
const decoded = decodeData(metadataSchema, metadataRecord);
const tableName = decoded[0];
if (tableName !== table.name) {
console.warn("Metadata table name does not match table ID", {
tableName,
tableId: table.toString(),
world: world.address,
});
}
const [fieldNames] = utils.defaultAbiCoder.decode(["string[]"], decoded[1]);
return { tableName, fieldNames };
});
Expand Down
27 changes: 22 additions & 5 deletions packages/network/src/workers/SyncWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ import { debug as parentDebug } from "./debug";
import { fetchStoreEvents } from "../v2/fetchStoreEvents";
import { abi as IStoreAbi } from "@latticexyz/store/abi/IStore.json";
import { Contract } from "ethers";
import { createModeClient } from "../v2/mode/createModeClient";
import { syncTablesFromMode } from "../v2/mode/syncTablesFromMode";
import { getModeBlockNumber } from "../v2/mode/getModeBlockNumber";

const debug = parentDebug.extend("SyncWorker");

Expand Down Expand Up @@ -127,6 +130,7 @@ export class SyncWorker<C extends Components> implements DoWork<Input, NetworkEv
);
const config = computedConfig.get();
const {
modeUrl,
snapshotServiceUrl,
streamServiceUrl,
chainId,
Expand All @@ -145,6 +149,7 @@ export class SyncWorker<C extends Components> implements DoWork<Input, NetworkEv
const { providers } = await createReconnectingProvider(computed(() => computedConfig.get().provider));
const provider = providers.get().json;
const snapshotClient = snapshotServiceUrl ? createSnapshotClient(snapshotServiceUrl) : undefined;
const modeClient = modeUrl ? createModeClient(modeUrl) : undefined;
const indexDbCache = await getIndexDbECSCache(chainId, worldContract.address);
const decode = createDecode(worldContract, provider);
const fetchWorldEvents = createFetchWorldEventsInBlockRange(
Expand Down Expand Up @@ -214,6 +219,8 @@ export class SyncWorker<C extends Components> implements DoWork<Input, NetworkEv
const cacheBlockNumber = !disableCache ? await getIndexDBCacheStoreBlockNumber(indexDbCache) : -1;
this.setLoadingState({ percentage: 50 });
const snapshotBlockNumber = await getSnapshotBlockNumber(snapshotClient, worldContract.address);
const modeBlockNumber = modeClient ? await getModeBlockNumber(modeClient, chainId) : -1;

this.setLoadingState({ percentage: 100 });
debug(
`cache block: ${cacheBlockNumber}, snapshot block: ${
Expand All @@ -222,13 +229,23 @@ export class SyncWorker<C extends Components> implements DoWork<Input, NetworkEv
);

let initialState = createCacheStore();
if (initialBlockNumber > Math.max(cacheBlockNumber, snapshotBlockNumber)) {
if (initialBlockNumber > Math.max(cacheBlockNumber, snapshotBlockNumber, modeBlockNumber)) {
// Skip initializing from cache/snapshot/mode if the initial block number is newer than all of them
initialState.blockNumber = initialBlockNumber;
} else {
// Load from cache if the snapshot is less than <cacheAgeThreshold> blocks newer than the cache
const syncFromSnapshot = snapshotClient && snapshotBlockNumber > cacheBlockNumber + cacheAgeThreshold;

if (syncFromSnapshot) {
// Load from cache if the mode/snapshot is less than <cacheAgeThreshold> blocks newer than the cache
const syncFromMode = modeClient && modeBlockNumber > cacheBlockNumber + cacheAgeThreshold;
const syncFromSnapshot =
!syncFromMode && snapshotClient && snapshotBlockNumber > cacheBlockNumber + cacheAgeThreshold;

if (syncFromMode) {
console.log("Initial sync from MODE");
this.setLoadingState({ state: SyncState.INITIAL, msg: "Fetching initial state from MODE", percentage: 0 });
initialState = await syncTablesFromMode(modeClient, chainId, storeContract, (percentage: number) =>
this.setLoadingState({ percentage })
);
this.setLoadingState({ percentage: 100 });
} else if (syncFromSnapshot) {
this.setLoadingState({ state: SyncState.INITIAL, msg: "Fetching initial state from snapshot", percentage: 0 });
initialState = await fetchSnapshotChunked(
snapshotClient,
Expand Down
6 changes: 4 additions & 2 deletions packages/recs/src/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ export function setComponent<S extends Schema, T = undefined>(
// Otherwise, we should let the user know we found undefined data.
console.warn(
"Component definition for",
component.metadata?.contractId ?? component.id,
component.metadata?.tableId ?? component.metadata?.contractId ?? component.id,
"is missing key",
key,
", ignoring value",
val,
"for entity",
entity
entity,
". Existing keys: ",
Object.keys(component.values)
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe("store/contractComponents", () => {
y: RecsType.Number;
},
{
contractId: string;
contractId: `0x${string}`;
tableId: string;
},
undefined
Expand Down
2 changes: 1 addition & 1 deletion packages/std-client/src/setup/setupMUDV2Network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export async function setupMUDV2Network<C extends ContractComponents>({
const storeSchemaTableId = new TableId("mudstore", "schema");
const storeSchemaComponent = defineComponent(
world,
{ schema: Type.T },
{ valueSchema: Type.String, keySchema: Type.String },
{
metadata: {
contractId: storeSchemaTableId.toHexString(),
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/v2/arrayToHex.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// TODO: migrate to viem's toHex()
export const arrayToHex = (array: Uint8Array | ArrayBuffer): string =>
export const arrayToHex = (array: Uint8Array | ArrayBuffer): `0x${string}` =>
`0x${[...new Uint8Array(array)].map((x) => x.toString(16).padStart(2, "0")).join("")}`;
Loading