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

refactor(cli): move off of old config #2941

Merged
merged 26 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this option ever worked - it wasn't part of builder method and isn't possible to pass in an object to CLI anyway.

};

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
Loading