Skip to content

Commit

Permalink
feat: add getRecords util and use instead of fetching logs in deployer
Browse files Browse the repository at this point in the history
  • Loading branch information
alvrs committed Nov 28, 2024
1 parent d17a9be commit 2bbe6bd
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 122 deletions.
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@latticexyz/protocol-parser": "workspace:*",
"@latticexyz/schema-type": "workspace:*",
"@latticexyz/store": "workspace:*",
"@latticexyz/store-sync": "workspace:*",
"@latticexyz/utils": "workspace:*",
"@latticexyz/world": "workspace:*",
"@latticexyz/world-module-metadata": "workspace:*",
Expand Down
25 changes: 8 additions & 17 deletions packages/cli/src/deploy/ensureResourceTags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import { getContractArtifact } from "../utils/getContractArtifact";
import { createPrepareDeploy } from "./createPrepareDeploy";
import { waitForTransactions } from "./waitForTransactions";
import { LibraryMap } from "./getLibraryMap";
import { fetchBlockLogs } from "@latticexyz/block-logs-stream";
import { getStoreLogs, flattenStoreLogs, logToRecord } from "@latticexyz/store/internal";
import { getKeyTuple, getSchemaPrimitives } from "@latticexyz/protocol-parser/internal";
import { getRecords } from "@latticexyz/store-sync";

const metadataModuleArtifact = getContractArtifact(metadataModule);

Expand All @@ -40,23 +39,15 @@ export async function ensureResourceTags<const value>({
? { readonly valueToHex?: (value: value) => Hex }
: { readonly valueToHex: (value: value) => Hex })): Promise<readonly Hex[]> {
debug("ensuring", tags.length, "resource tags");

debug("looking up existing resource tags");
const blockLogs = await fetchBlockLogs({
fromBlock: worldDeploy.deployBlock,
toBlock: worldDeploy.stateBlock,
maxBlockRange: 100_000n,
async getLogs({ fromBlock, toBlock }) {
return getStoreLogs(client, {
address: worldDeploy.address,
fromBlock,
toBlock,
tableId: metadataConfig.tables.metadata__ResourceTag.tableId,
});
},

const { records } = await getRecords({
table: metadataConfig.tables.metadata__ResourceTag,
worldAddress: worldDeploy.address,
indexerUrl: "https://indexer.mud.garnetchain.com",
chainId: 17069,
});
const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs));
const records = logs.map((log) => logToRecord({ log, table: metadataConfig.tables.metadata__ResourceTag }));

debug("found", records.length, "resource tags");

const existingTags = new Map(
Expand Down
54 changes: 11 additions & 43 deletions packages/cli/src/deploy/getResourceAccess.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,28 @@
import { Client, Hex, Address, getAddress } from "viem";
import { WorldDeploy } from "./common";
import { debug } from "./debug";
import { decodeKey, getKeySchema, getSchemaTypes } from "@latticexyz/protocol-parser/internal";
import { getTableValue } from "./getTableValue";
import worldConfig from "@latticexyz/world/mud.config";
import { fetchBlockLogs } from "@latticexyz/block-logs-stream";
import { flattenStoreLogs, getStoreLogs } from "@latticexyz/store/internal";
import { getRecords } from "@latticexyz/store-sync";

export async function getResourceAccess({
client,
worldDeploy,
}: {
readonly client: Client;
readonly worldDeploy: WorldDeploy;
}): Promise<readonly { readonly resourceId: Hex; readonly address: Address }[]> {
debug("looking up resource access for", worldDeploy.address);

const blockLogs = await fetchBlockLogs({
fromBlock: worldDeploy.deployBlock,
toBlock: worldDeploy.stateBlock,
maxBlockRange: 100_000n,
async getLogs({ fromBlock, toBlock }) {
return getStoreLogs(client, {
address: worldDeploy.address,
fromBlock,
toBlock,
tableId: worldConfig.namespaces.world.tables.ResourceAccess.tableId,
});
},
const { records } = await getRecords({
table: worldConfig.namespaces.world.tables.ResourceAccess,
worldAddress: worldDeploy.address,
indexerUrl: "https://indexer.mud.garnetchain.com",
chainId: 17069,
});
const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs));

const keys = logs.map((log) =>
decodeKey(getSchemaTypes(getKeySchema(worldConfig.namespaces.world.tables.ResourceAccess)), log.args.keyTuple),
);

const access = (
await Promise.all(
keys.map(
async (key) =>
[
key,
await getTableValue({
client,
worldDeploy,
table: worldConfig.namespaces.world.tables.ResourceAccess,
key,
}),
] as const,
),
)
)
.filter(([, value]) => value.access)
.map(([key]) => ({
resourceId: key.resourceId,
address: getAddress(key.caller),
const access = records
.filter((record) => record.access)
.map((record) => ({
resourceId: record.resourceId,
address: getAddress(record.caller),
}));

debug("found", access.length, "resource<>address access pairs");
Expand Down
26 changes: 7 additions & 19 deletions packages/cli/src/deploy/getResourceIds.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,24 @@
import { Client, Hex } from "viem";
import { flattenStoreLogs, getStoreLogs } from "@latticexyz/store/internal";
import { WorldDeploy } from "./common";
import { debug } from "./debug";
import storeConfig from "@latticexyz/store/mud.config";
import { fetchBlockLogs } from "@latticexyz/block-logs-stream";
import { getRecords } from "@latticexyz/store-sync";

export async function getResourceIds({
client,
worldDeploy,
}: {
readonly client: Client;
readonly worldDeploy: WorldDeploy;
}): Promise<readonly Hex[]> {
debug("looking up resource IDs for", worldDeploy.address);

const blockLogs = await fetchBlockLogs({
fromBlock: worldDeploy.deployBlock,
toBlock: worldDeploy.stateBlock,
maxBlockRange: 100_000n,
async getLogs({ fromBlock, toBlock }) {
return getStoreLogs(client, {
address: worldDeploy.address,
fromBlock,
toBlock,
tableId: storeConfig.namespaces.store.tables.ResourceIds.tableId,
});
},
const { records } = await getRecords({
table: storeConfig.namespaces.store.tables.ResourceIds,
worldAddress: worldDeploy.address,
indexerUrl: "https://indexer.mud.garnetchain.com",
chainId: 17069,
});
const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs));
const resourceIds = records.map((record) => record.resourceId);

const resourceIds = logs.map((log) => log.args.keyTuple[0]);
debug("found", resourceIds.length, "resource IDs for", worldDeploy.address);

return resourceIds;
}
59 changes: 16 additions & 43 deletions packages/cli/src/deploy/getTables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,10 @@ import { Client, Hex, decodeAbiParameters, parseAbiParameters } from "viem";
import { hexToResource } from "@latticexyz/common";
import { WorldDeploy } from "./common";
import { debug } from "./debug";
import {
decodeKey,
decodeValueArgs,
getKeySchema,
getSchemaTypes,
getValueSchema,
hexToSchema,
} from "@latticexyz/protocol-parser/internal";
import { hexToSchema } from "@latticexyz/protocol-parser/internal";
import { Schema, Table } from "@latticexyz/config";
import storeConfig from "@latticexyz/store/mud.config";
import { fetchBlockLogs } from "@latticexyz/block-logs-stream";
import { flattenStoreLogs, getStoreLogs } from "@latticexyz/store/internal";
import { getRecords } from "@latticexyz/store-sync";

// TODO: add label and namespaceLabel once we register it onchain
type DeployedTable = Omit<Table, "label" | "namespaceLabel"> & {
Expand All @@ -24,47 +16,28 @@ type DeployedTable = Omit<Table, "label" | "namespaceLabel"> & {
};

export async function getTables({
client,
worldDeploy,
}: {
readonly client: Client;
readonly worldDeploy: WorldDeploy;
}): Promise<readonly Omit<DeployedTable, "label">[]> {
debug("looking up tables for", worldDeploy.address);

const blockLogs = await fetchBlockLogs({
fromBlock: worldDeploy.deployBlock,
toBlock: worldDeploy.stateBlock,
maxBlockRange: 100_000n,
async getLogs({ fromBlock, toBlock }) {
return getStoreLogs(client, {
address: worldDeploy.address,
fromBlock,
toBlock,
tableId: storeConfig.namespaces.store.tables.Tables.tableId,
});
},
const { records } = await getRecords({
table: storeConfig.namespaces.store.tables.Tables,
worldAddress: worldDeploy.address,
indexerUrl: "https://indexer.mud.garnetchain.com",
chainId: 17069,
});
const logs = flattenStoreLogs(blockLogs.flatMap((block) => block.logs));

// TODO: combine with store-sync logToTable and export from somewhere
const tables = logs.map((log): DeployedTable => {
const { tableId } = decodeKey(
getSchemaTypes(getKeySchema(storeConfig.namespaces.store.tables.Tables)),
log.args.keyTuple,
);
const { type, namespace, name } = hexToResource(tableId);
const recordValue = decodeValueArgs(
getSchemaTypes(getValueSchema(storeConfig.namespaces.store.tables.Tables)),
log.args,
);
const tables = records.map((record) => {
const { type, namespace, name } = hexToResource(record.tableId);

const keySchemaHex = recordValue.keySchema;
const valueSchemaHex = recordValue.valueSchema;
const solidityKeySchema = hexToSchema(keySchemaHex);
const solidityValueSchema = hexToSchema(valueSchemaHex);
const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), recordValue.abiEncodedKeyNames)[0];
const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), recordValue.abiEncodedFieldNames)[0];
const solidityKeySchema = hexToSchema(record.keySchema);
const solidityValueSchema = hexToSchema(record.valueSchema);
const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), record.abiEncodedKeyNames)[0];
const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), record.abiEncodedFieldNames)[0];

const valueAbiTypes = [...solidityValueSchema.staticFields, ...solidityValueSchema.dynamicFields];

Expand All @@ -80,13 +53,13 @@ export async function getTables({
type: type as never,
namespace,
name,
tableId,
tableId: record.tableId,
schema: { ...keySchema, ...valueSchema },
key: Object.keys(keySchema),
keySchema,
keySchemaHex,
keySchemaHex: record.keySchema,
valueSchema,
valueSchemaHex,
valueSchemaHex: record.valueSchema,
};
});

Expand Down
70 changes: 70 additions & 0 deletions packages/store-sync/src/getRecords.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { fetchBlockLogs } from "@latticexyz/block-logs-stream";
import { Table } from "@latticexyz/config";
import { getSchemaPrimitives } from "@latticexyz/protocol-parser/internal";
import { LogToRecordArgs, flattenStoreLogs, getStoreLogs, logToRecord } from "@latticexyz/store/internal";
import { Address, createPublicClient, http } from "viem";
import { debug } from "./debug";
import { getSnapshot } from "./getSnapshot";
import { StorageAdapterLog } from "./common";

type GetRecordsOptions<table extends Table = Table> = {
table: table;
worldAddress: Address;
} & (
| {
indexerUrl: string;
chainId: number;
}
| {
rpcUrl: string;
fromBlock?: bigint;
toBlock?: bigint;
}
);

type GetRecordsResult<table extends Table = Table> = {
records: getSchemaPrimitives<table["schema"]>[];
blockNumber: bigint;
};

export async function getRecords<table extends Table>(
options: GetRecordsOptions<table>,
): Promise<GetRecordsResult<table>> {
async function getLogs(): Promise<readonly StorageAdapterLog[]> {
if ("indexerUrl" in options) {
debug("fetching records for", options.table.label, "via indexer from", options.indexerUrl);
const logs = await getSnapshot({
chainId: options.chainId,
address: options.worldAddress,
indexerUrl: options.indexerUrl,
filters: [{ tableId: options.table.tableId }],
});
return logs?.logs ?? [];
} else {
debug("fetching records for", options.table.label, "via RPC from", options.rpcUrl);
const client = createPublicClient({
transport: http(options.rpcUrl),
});
const blockLogs = await fetchBlockLogs({
fromBlock: options.fromBlock ?? 0n,
toBlock: options.toBlock ?? (await client.getBlockNumber()),
maxBlockRange: 100_000n,
async getLogs({ fromBlock, toBlock }) {
return getStoreLogs(client, {
address: options.worldAddress,
fromBlock,
toBlock,
tableId: options.table.tableId,
});
},
});
return flattenStoreLogs(blockLogs.flatMap((block) => block.logs));
}
}

const logs = await getLogs();
const records = logs.map((log) => logToRecord({ log: log as LogToRecordArgs<table>["log"], table: options.table }));
const blockNumber = logs.length > 0 ? logs[logs.length - 1].blockNumber ?? 0n : 0n;
debug("found", records.length, "records for table", options.table.label, "at block", blockNumber);
return { records, blockNumber };
}
1 change: 1 addition & 0 deletions packages/store-sync/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export * from "./logToTable";
export * from "./tablesWithRecordsToLogs";
export * from "./tableToLog";
export * from "./recordToLog";
export * from "./getRecords";

export { logToRecord, type LogToRecordArgs } from "@latticexyz/store/internal";
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2bbe6bd

Please sign in to comment.