Skip to content

Commit

Permalink
feat(cli): register resource labels (#3028)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored Aug 15, 2024
1 parent fad4e85 commit 91028bf
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 11 deletions.
7 changes: 5 additions & 2 deletions packages/cli/src/deploy/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export type Library = DeterministicContract & {
};

export type System = DeterministicContract & {
// TODO: add label
readonly label: string;
readonly namespace: string;
readonly name: string;
readonly systemId: Hex;
Expand All @@ -92,7 +92,10 @@ export type System = DeterministicContract & {
readonly worldFunctions: readonly WorldFunction[];
};

export type DeployedSystem = Omit<System, "abi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds"> & {
export type DeployedSystem = Omit<
System,
"label" | "abi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds"
> & {
address: Address;
};

Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/deploy/configToModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export async function configToModules<config extends World>(
forgeOutDir: string,
): Promise<readonly Module[]> {
const defaultModules: Module[] = [
// TODO: replace metadata install here with custom logic inside `ensureModules` or an `ensureDefaultModules` to check
// if metadata namespace exists, if we own it, and if so transfer ownership to the module before reinstalling
// (https://github.com/latticexyz/mud/issues/3035)
{
optional: true,
name: "MetadataModule",
Expand Down
14 changes: 12 additions & 2 deletions packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ensureContractsDeployed } from "./ensureContractsDeployed";
import { randomBytes } from "crypto";
import { ensureWorldFactory } from "./ensureWorldFactory";
import { Table } from "@latticexyz/config";
import { ensureResourceLabels } from "./ensureResourceLabels";

type DeployOptions = {
client: Client<Transport, Chain | undefined, Account>;
Expand Down Expand Up @@ -89,10 +90,14 @@ export async function deploy({
throw new Error(`Unsupported World version: ${worldDeploy.worldVersion}`);
}

const resources = [
...tables.map((table) => ({ resourceId: table.tableId, label: table.label })),
...systems.map((system) => ({ resourceId: system.systemId, label: system.label })),
];
const namespaceTxs = await ensureNamespaceOwner({
client,
worldDeploy,
resourceIds: [...tables.map((table) => table.tableId), ...systems.map((system) => system.systemId)],
resourceIds: resources.map(({ resourceId }) => resourceId),
});

debug("waiting for all namespace registration transactions to confirm");
Expand Down Expand Up @@ -124,8 +129,13 @@ export async function deploy({
worldDeploy,
modules,
});
const labelTxs = await ensureResourceLabels({
client,
worldDeploy,
resources,
});

const txs = [...tableTxs, ...systemTxs, ...functionTxs, ...moduleTxs];
const txs = [...tableTxs, ...systemTxs, ...functionTxs, ...moduleTxs, ...labelTxs];

// wait for each tx separately/serially, because parallelizing results in RPC errors
debug("waiting for all transactions to confirm");
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/ensureNamespaceOwner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export async function ensureNamespaceOwner({
// Register missing namespaces
const missingNamespaces = desiredNamespaces.filter((namespace) => !existingNamespaces.has(namespace));
if (missingNamespaces.length > 0) {
debug("registering namespaces", Array.from(missingNamespaces).join(", "));
debug("registering namespaces:", Array.from(missingNamespaces).join(", "));
}
const registrationTxs = Promise.all(
missingNamespaces.map((namespace) =>
Expand Down
66 changes: 66 additions & 0 deletions packages/cli/src/deploy/ensureResourceLabels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Hex, Client, Transport, Chain, Account, stringToHex, BaseError, hexToString } from "viem";
import { WorldDeploy } from "./common";
import { debug } from "./debug";
import { hexToResource, writeContract } from "@latticexyz/common";
import { isDefined } from "@latticexyz/common/utils";
import metadataConfig from "@latticexyz/world-module-metadata/mud.config";
import metadataAbi from "@latticexyz/world-module-metadata/out/IMetadataSystem.sol/IMetadataSystem.abi.json" assert { type: "json" };
import { getTableValue } from "./getTableValue";

type LabeledResource = {
readonly resourceId: Hex;
readonly label: string;
};

export async function ensureResourceLabels({
client,
worldDeploy,
resources,
}: {
readonly client: Client<Transport, Chain | undefined, Account>;
readonly worldDeploy: WorldDeploy;
readonly resources: readonly LabeledResource[];
}): Promise<readonly Hex[]> {
const currentLabels = await Promise.all(
resources.map(async (resource) => {
const { value } = await getTableValue({
client,
worldDeploy,
table: metadataConfig.tables.metadata__ResourceTag,
key: { resource: resource.resourceId, tag: stringToHex("label", { size: 32 }) },
});
return hexToString(value);
}),
);

const resourcesToSet = resources.filter((resource, i) => resource.label !== currentLabels[i]);
if (resourcesToSet.length === 0) {
return [];
}

debug(`setting ${resources.length} resource labels`);
return (
await Promise.all(
resourcesToSet.map(async ({ resourceId, label }) => {
const resource = hexToResource(resourceId);
// TODO: move to resourceToDebugString
const resourceString = `${resource.type}:${resource.namespace}:${resource.name}`;
debug(`setting resource label for ${resourceString} to ${label}`);
try {
return await writeContract(client, {
chain: client.chain ?? null,
address: worldDeploy.address,
abi: metadataAbi,
// TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
functionName: "metadata__setResourceTag",
args: [resourceId, stringToHex("label", { size: 32 }), stringToHex(label)],
});
} catch (error) {
debug(
`failed to set resource label for ${resourceString}, skipping\n ${error instanceof BaseError ? error.shortMessage : error}`,
);
}
}),
)
).filter(isDefined);
}
6 changes: 3 additions & 3 deletions packages/cli/src/deploy/ensureSystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export async function ensureSystems({
),
);
if (existingSystems.length) {
debug("existing systems", existingSystems.map(resourceToLabel).join(", "));
debug("existing systems:", existingSystems.map(resourceToLabel).join(", "));
}
const existingSystemIds = existingSystems.map((system) => system.systemId);

Expand All @@ -53,14 +53,14 @@ export async function ensureSystems({
),
);
if (systemsToUpgrade.length) {
debug("upgrading systems", systemsToUpgrade.map(resourceToLabel).join(", "));
debug("upgrading systems:", systemsToUpgrade.map(resourceToLabel).join(", "));
}

const systemsToAdd = missingSystems.filter(
(system) => !worldSystems.some((worldSystem) => worldSystem.systemId === system.systemId),
);
if (systemsToAdd.length) {
debug("registering new systems", systemsToAdd.map(resourceToLabel).join(", "));
debug("registering new systems:", systemsToAdd.map(resourceToLabel).join(", "));
}

await ensureContractsDeployed({
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/deploy/ensureTables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ export async function ensureTables({

const existingTables = tables.filter((table) => worldTableIds.includes(table.tableId));
if (existingTables.length) {
debug("existing tables", existingTables.map(resourceToLabel).join(", "));
debug("existing tables:", existingTables.map(resourceToLabel).join(", "));
}

const missingTables = tables.filter((table) => !worldTableIds.includes(table.tableId));
if (missingTables.length) {
debug("registering tables", missingTables.map(resourceToLabel).join(", "));
debug("registering tables:", missingTables.map(resourceToLabel).join(", "));
return await Promise.all(
missingTables.map((table) => {
const keySchema = getSchemaTypes(getKeySchema(table));
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/getSystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export async function getSystems({
]);
const systems = resourceIds.map(hexToResource).filter((resource) => resource.type === "system");

debug("looking up systems", systems.map(resourceToLabel).join(", "));
debug("looking up systems:", systems.map(resourceToLabel).join(", "));
return await Promise.all(
systems.map(async (system): Promise<DeployedSystem> => {
const { system: address, publicAccess } = await getTableValue({
Expand Down

0 comments on commit 91028bf

Please sign in to comment.