Skip to content

Commit

Permalink
refactor(cli): move off of old config (#2941)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored Jul 16, 2024
1 parent ab985f2 commit 3cbbc62
Show file tree
Hide file tree
Showing 52 changed files with 452 additions and 676 deletions.
7 changes: 7 additions & 0 deletions .changeset/fuzzy-maps-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@latticexyz/cli": patch
---

Refactored package to use the new Store/World configs under the hood, removing compatibility layers.

Removed `--srcDir` option from all commands in favor of using `sourceDirectory` in the project's MUD config.
5 changes: 5 additions & 0 deletions .changeset/lazy-impalas-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/world": patch
---

Refactored how worldgen resolves systems from the config and filesystem.
7 changes: 7 additions & 0 deletions .changeset/old-waves-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@latticexyz/world-modules": patch
---

Moved build scripts to `mud build` now that CLI doesn't depend on this package.

Removed generated world interfaces as this package isn't meant to be used as a "world", but as a set of individual modules.
1 change: 1 addition & 0 deletions packages/cli/contracts/src/codegen/common.sol

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

10 changes: 1 addition & 9 deletions packages/cli/src/build.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import path from "node:path";
import { tablegen } from "@latticexyz/store/codegen";
import { worldgen } from "@latticexyz/world/node";
import { World as WorldConfig } from "@latticexyz/world";
import { forge, getRemappings } from "@latticexyz/common/foundry";
import { getExistingContracts } from "./utils/getExistingContracts";
import { execa } from "execa";

type BuildOptions = {
foundryProfile?: string;
srcDir: string;
/**
* MUD project root directory where all other relative paths are resolved from.
*
Expand All @@ -21,16 +18,11 @@ type BuildOptions = {
export async function build({
rootDir,
config,
srcDir,
foundryProfile = process.env.FOUNDRY_PROFILE,
}: BuildOptions): Promise<void> {
const outPath = path.join(srcDir, config.codegen.outputDirectory);
const remappings = await getRemappings(foundryProfile);

await Promise.all([
tablegen({ rootDir, config, remappings }),
worldgen(config, getExistingContracts(srcDir), outPath),
]);
await Promise.all([tablegen({ rootDir, config, remappings }), worldgen({ rootDir, config })]);

await forge(["build"], { profile: foundryProfile });
await execa("mud", ["abi-ts"], { stdio: "inherit" });
Expand Down
4 changes: 1 addition & 3 deletions packages/cli/src/commands/build.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CommandModule } from "yargs";
import { loadConfig, resolveConfigPath } from "@latticexyz/config/node";
import { World as WorldConfig } from "@latticexyz/world";
import { getSrcDirectory } from "@latticexyz/common/foundry";
import { build } from "../build";
import path from "node:path";

Expand All @@ -25,9 +24,8 @@ const commandModule: CommandModule<Options, Options> = {
async handler(opts) {
const configPath = await resolveConfigPath(opts.configPath);
const config = (await loadConfig(configPath)) as WorldConfig;
const srcDir = await getSrcDirectory();

await build({ rootDir: path.dirname(configPath), config, srcDir, foundryProfile: opts.profile });
await build({ rootDir: path.dirname(configPath), config, foundryProfile: opts.profile });

process.exit(0);
},
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/commands/dev-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
saveDeployment: true,
deployerAddress: undefined,
worldAddress,
srcDir,
salt: "0x",
kms: undefined,
});
Expand Down
65 changes: 25 additions & 40 deletions packages/cli/src/commands/trace.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
import { existsSync, readFileSync } from "fs";
import path from "node:path";
import { existsSync, readFileSync } from "node:fs";
import type { CommandModule } from "yargs";
import { ethers } from "ethers";

import { loadConfig } from "@latticexyz/config/node";
import { loadConfig, resolveConfigPath } from "@latticexyz/config/node";
import { MUDError } from "@latticexyz/common/errors";
import { cast, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
import { resolveWorldConfig } from "@latticexyz/world/internal";
import { cast, getRpcUrl } from "@latticexyz/common/foundry";
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json" assert { type: "json" };
import worldConfig from "@latticexyz/world/mud.config";
import { resourceToHex } from "@latticexyz/common";
import { getExistingContracts } from "../utils/getExistingContracts";
import { createClient, http } from "viem";
import { getChainId } from "viem/actions";
import { World as WorldConfig } from "@latticexyz/world";
import { worldToV1 } from "@latticexyz/world/config/v2";
import { resolveSystems } from "@latticexyz/world/node";

// TODO account for multiple namespaces (https://github.com/latticexyz/mud/issues/994)
const systemsTableId = resourceToHex({
type: "system",
namespace: worldConfig.namespace,
name: worldConfig.tables.world__Systems.name,
});
const systemsTableId = worldConfig.tables.world__Systems.tableId;

type Options = {
tx: string;
worldAddress?: string;
configPath?: string;
profile?: string;
srcDir?: string;
rpc?: string;
};

Expand All @@ -45,56 +37,49 @@ const commandModule: CommandModule<Options, Options> = {
},
configPath: { type: "string", description: "Path to the MUD config file" },
profile: { type: "string", description: "The foundry profile to use" },
srcDir: { type: "string", description: "Source directory. Defaults to foundry src directory." },
rpc: { type: "string", description: "json rpc endpoint. Defaults to foundry's configured eth_rpc_url" },
});
},

async handler(args) {
args.profile ??= process.env.FOUNDRY_PROFILE;
const { profile } = args;
args.srcDir ??= await getSrcDirectory(profile);
args.rpc ??= await getRpcUrl(profile);
const { tx, configPath, srcDir, rpc } = args;
const configPath = await resolveConfigPath(args.configPath);
const rootDir = path.dirname(configPath);

const existingContracts = getExistingContracts(srcDir);
const profile = args.profile ?? process.env.FOUNDRY_PROFILE;
const rpc = args.rpc ?? (await getRpcUrl(profile));

// Load the config
const configV2 = (await loadConfig(configPath)) as WorldConfig;
const mudConfig = worldToV1(configV2);

const resolvedConfig = resolveWorldConfig(
mudConfig,
existingContracts.map(({ basename }) => basename),
);
const config = (await loadConfig(configPath)) as WorldConfig;

// Get worldAddress either from args or from worldsFile
const worldAddress = args.worldAddress ?? (await getWorldAddress(mudConfig.worldsFile, rpc));
const worldAddress = args.worldAddress ?? (await getWorldAddress(config.deploy.worldsFile, rpc));

// Create World contract instance from deployed address
const provider = new ethers.providers.StaticJsonRpcProvider(rpc);
const WorldContract = new ethers.Contract(worldAddress, IBaseWorldAbi, provider);

// TODO account for multiple namespaces (https://github.com/latticexyz/mud/issues/994)
const namespace = mudConfig.namespace;
const names = Object.values(resolvedConfig.systems).map(({ name }) => name);
// TODO: replace with system.namespace
const namespace = config.namespace;
const systems = await resolveSystems({ rootDir, config });

// Fetch system table field layout from chain
const systemTableFieldLayout = await WorldContract.getFieldLayout(systemsTableId);
const labels: { name: string; address: string }[] = [];
for (const name of names) {
const systemSelector = resourceToHex({ type: "system", namespace, name });
// Get the first field of `Systems` table (the table maps system name to its address and other data)
const address = await WorldContract.getField(systemsTableId, [systemSelector], 0, systemTableFieldLayout);
labels.push({ name, address });
}
const labels = await Promise.all(
systems.map(async (system) => {
// TODO: replace with system.systemId
const systemId = resourceToHex({ type: "system", namespace, name: system.name });
// Get the first field of `Systems` table (the table maps system name to its address and other data)
const address = await WorldContract.getField(systemsTableId, [systemId], 0, systemTableFieldLayout);
return { name: system.name, address };
}),
);

const result = await cast([
"run",
"--label",
`${worldAddress}:World`,
...labels.map(({ name, address }) => ["--label", `${address}:${name}`]).flat(),
`${tx}`,
`${args.tx}`,
]);
console.log(result);

Expand Down
31 changes: 14 additions & 17 deletions packages/cli/src/commands/verify.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { CommandModule, InferredOptionTypes } from "yargs";
import { verify } from "../verify";
import { loadConfig } from "@latticexyz/config/node";
import { loadConfig, resolveConfigPath } from "@latticexyz/config/node";
import { World as WorldConfig } from "@latticexyz/world";
import { resolveWorldConfig } from "@latticexyz/world/internal";
import { worldToV1 } from "@latticexyz/world/config/v2";
import { getOutDirectory, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
import { getExistingContracts } from "../utils/getExistingContracts";
import { resolveSystems } from "@latticexyz/world/node";
import { getOutDirectory, getRpcUrl } from "@latticexyz/common/foundry";
import { getContractData } from "../utils/getContractData";
import { Hex, createWalletClient, http } from "viem";
import chalk from "chalk";
import { configToModules } from "../deploy/configToModules";
import path from "node:path";

const verifyOptions = {
deployerAddress: {
Expand All @@ -24,7 +23,6 @@ const verifyOptions = {
type: "boolean",
desc: "Enable batch processing of RPC requests in viem client (defaults to batch size of 100 and wait of 1s)",
},
srcDir: { type: "string", desc: "Source directory. Defaults to foundry src directory." },
verifier: { type: "string", desc: "The verifier to use. Defaults to blockscout", default: "blockscout" },
verifierUrl: {
type: "string",
Expand All @@ -46,10 +44,11 @@ const commandModule: CommandModule<Options, Options> = {
async handler(opts) {
const profile = opts.profile ?? process.env.FOUNDRY_PROFILE;

const configV2 = (await loadConfig(opts.configPath)) as WorldConfig;
const config = worldToV1(configV2);
const configPath = await resolveConfigPath(opts.configPath);
const rootDir = path.dirname(configPath);

const config = (await loadConfig(configPath)) as WorldConfig;

const srcDir = opts.srcDir ?? (await getSrcDirectory(profile));
const outDir = await getOutDirectory(profile);

const rpc = opts.rpc ?? (await getRpcUrl(profile));
Expand All @@ -70,19 +69,17 @@ const commandModule: CommandModule<Options, Options> = {
}),
});

const contractNames = getExistingContracts(srcDir).map(({ basename }) => basename);
const resolvedWorldConfig = resolveWorldConfig(config, contractNames);

const systems = Object.keys(resolvedWorldConfig.systems).map((name) => {
const contractData = getContractData(`${name}.sol`, name, outDir);

// TODO: replace with `resolveConfig` and support for linked libs
const configSystems = await resolveSystems({ rootDir, config });
const systems = configSystems.map((system) => {
const contractData = getContractData(`${system.name}.sol`, system.name, outDir);
return {
name,
name: system.name,
bytecode: contractData.bytecode,
};
});

const modules = await configToModules(configV2, outDir);
const modules = await configToModules(config, outDir);

await verify({
client,
Expand Down
28 changes: 6 additions & 22 deletions packages/cli/src/commands/worldgen.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import path from "node:path";
import type { CommandModule } from "yargs";
import { loadConfig } from "@latticexyz/config/node";
import { loadConfig, resolveConfigPath } from "@latticexyz/config/node";
import { World as WorldConfig } from "@latticexyz/world";
import { worldgen } from "@latticexyz/world/node";
import { getSrcDirectory } from "@latticexyz/common/foundry";
import path from "path";
import { rmSync } from "fs";
import { getExistingContracts } from "../utils/getExistingContracts";

type Options = {
configPath?: string;
clean?: boolean;
srcDir?: string;
config?: WorldConfig;
};

const commandModule: CommandModule<Options, Options> = {
Expand All @@ -37,22 +32,11 @@ const commandModule: CommandModule<Options, Options> = {
};

export async function worldgenHandler(args: Options) {
const srcDir = args.srcDir ?? (await getSrcDirectory());
const configPath = await resolveConfigPath(args.configPath);
const config = (await loadConfig(configPath)) as WorldConfig;
const rootDir = path.dirname(configPath);

const existingContracts = getExistingContracts(srcDir);

// Load the config
const mudConfig = args.config ?? ((await loadConfig(args.configPath)) as WorldConfig);

const outputBaseDirectory = path.join(srcDir, mudConfig.codegen.outputDirectory);

// clear the worldgen directory
if (args.clean) {
rmSync(path.join(outputBaseDirectory, mudConfig.codegen.worldgenDirectory), { recursive: true, force: true });
}

// generate new interfaces
await worldgen(mudConfig, existingContracts, outputBaseDirectory);
await worldgen({ rootDir, config, clean: args.clean });
}

export default commandModule;
18 changes: 3 additions & 15 deletions packages/cli/src/deploy/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@ import { Abi, Address, Hex, padHex } from "viem";
import storeConfig from "@latticexyz/store/mud.config";
import worldConfig from "@latticexyz/world/mud.config";
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json" assert { type: "json" };
import { Tables, configToTables } from "./configToTables";
import { helloStoreEvent } from "@latticexyz/store";
import { StoreConfig } from "@latticexyz/store/internal";
import { helloWorldEvent } from "@latticexyz/world";
import { WorldConfig } from "@latticexyz/world/internal";
import { storeToV1 } from "@latticexyz/store/config/v2";
import { worldToV1 } from "@latticexyz/world/config/v2";

export const salt = padHex("0x", { size: 32 });

// https://eips.ethereum.org/EIPS/eip-170
export const contractSizeLimit = parseInt("6000", 16);

// TODO: add `as const` to mud config so these get more strongly typed (blocked by current config parsing not using readonly)
export const storeTables = configToTables(storeToV1(storeConfig));
export const worldTables = configToTables(worldToV1(worldConfig));
export const storeTables = storeConfig.tables;
export const worldTables = worldConfig.tables;

export const worldDeployEvents = [helloStoreEvent, helloWorldEvent] as const;

Expand Down Expand Up @@ -93,6 +87,7 @@ export type Library = DeterministicContract & {
};

export type System = DeterministicContract & {
// TODO: add label
readonly namespace: string;
readonly name: string;
readonly systemId: Hex;
Expand All @@ -111,10 +106,3 @@ export type Module = DeterministicContract & {
readonly installAsRoot: boolean;
readonly installData: Hex; // TODO: figure out better naming for this
};

export type ConfigInput = StoreConfig & WorldConfig;
export type Config<config extends ConfigInput> = {
readonly tables: Tables<config>;
readonly systems: readonly System[];
readonly libraries: readonly Library[];
};
Loading

0 comments on commit 3cbbc62

Please sign in to comment.