@latticexyz/cli 2.0.0-next.13 → 2.0.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands-ORXW7F62.js +27 -0
- package/dist/commands-ORXW7F62.js.map +1 -0
- package/dist/mud.js +1 -1
- package/package.json +12 -12
- package/src/build.ts +44 -0
- package/src/commands/build.ts +36 -0
- package/src/commands/dev-contracts.ts +2 -1
- package/src/commands/index.ts +2 -0
- package/src/commands/set-version.ts +4 -1
- package/src/commands/trace.ts +1 -1
- package/src/debug.ts +7 -0
- package/src/deploy/common.ts +6 -2
- package/src/deploy/debug.ts +7 -0
- package/src/deploy/deploy.ts +4 -4
- package/src/deploy/ensureContract.ts +14 -2
- package/src/deploy/ensureModules.ts +1 -0
- package/src/deploy/ensureSystems.ts +1 -0
- package/src/deploy/ensureWorldFactory.ts +18 -5
- package/src/deploy/getSystems.ts +1 -1
- package/src/deploy/resolveConfig.ts +4 -7
- package/src/runDeploy.ts +3 -10
- package/src/utils/modules/constants.ts +10 -6
- package/src/utils/utils/getContractData.ts +9 -3
- package/dist/commands-AAHOIIJW.js +0 -23
- package/dist/commands-AAHOIIJW.js.map +0 -1
package/src/build.ts
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
2
|
+
import path from "node:path";
|
3
|
+
import { tablegen } from "@latticexyz/store/codegen";
|
4
|
+
import { worldgen } from "@latticexyz/world/node";
|
5
|
+
import { StoreConfig } from "@latticexyz/store";
|
6
|
+
import { WorldConfig } from "@latticexyz/world";
|
7
|
+
import { forge, getForgeConfig, getRemappings } from "@latticexyz/common/foundry";
|
8
|
+
import { getExistingContracts } from "./utils/getExistingContracts";
|
9
|
+
import { debug as parentDebug } from "./debug";
|
10
|
+
import { execa } from "execa";
|
11
|
+
|
12
|
+
const debug = parentDebug.extend("runDeploy");
|
13
|
+
|
14
|
+
type BuildOptions = {
|
15
|
+
foundryProfile?: string;
|
16
|
+
srcDir: string;
|
17
|
+
config: StoreConfig & WorldConfig;
|
18
|
+
};
|
19
|
+
|
20
|
+
export async function build({
|
21
|
+
config,
|
22
|
+
srcDir,
|
23
|
+
foundryProfile = process.env.FOUNDRY_PROFILE,
|
24
|
+
}: BuildOptions): Promise<void> {
|
25
|
+
const outPath = path.join(srcDir, config.codegenDirectory);
|
26
|
+
const remappings = await getRemappings(foundryProfile);
|
27
|
+
await Promise.all([tablegen(config, outPath, remappings), worldgen(config, getExistingContracts(srcDir), outPath)]);
|
28
|
+
|
29
|
+
// TODO remove when https://github.com/foundry-rs/foundry/issues/6241 is resolved
|
30
|
+
const forgeConfig = await getForgeConfig(foundryProfile);
|
31
|
+
if (forgeConfig.cache) {
|
32
|
+
const cacheFilePath = path.join(forgeConfig.cache_path, "solidity-files-cache.json");
|
33
|
+
if (existsSync(cacheFilePath)) {
|
34
|
+
debug("Unsetting cached content hash of IWorld.sol to force it to regenerate");
|
35
|
+
const solidityFilesCache = JSON.parse(readFileSync(cacheFilePath, "utf8"));
|
36
|
+
const worldInterfacePath = path.join(outPath, "world", "IWorld.sol");
|
37
|
+
solidityFilesCache["files"][worldInterfacePath]["contentHash"] = "";
|
38
|
+
writeFileSync(cacheFilePath, JSON.stringify(solidityFilesCache, null, 2));
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
await forge(["build"], { profile: foundryProfile });
|
43
|
+
await execa("mud", ["abi-ts"], { stdio: "inherit" });
|
44
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import type { CommandModule } from "yargs";
|
2
|
+
import { loadConfig } from "@latticexyz/config/node";
|
3
|
+
import { StoreConfig } from "@latticexyz/store";
|
4
|
+
import { WorldConfig } from "@latticexyz/world";
|
5
|
+
|
6
|
+
import { getSrcDirectory } from "@latticexyz/common/foundry";
|
7
|
+
import { build } from "../build";
|
8
|
+
|
9
|
+
type Options = {
|
10
|
+
configPath?: string;
|
11
|
+
profile?: string;
|
12
|
+
};
|
13
|
+
|
14
|
+
const commandModule: CommandModule<Options, Options> = {
|
15
|
+
command: "build",
|
16
|
+
|
17
|
+
describe: "Build contracts and generate MUD artifacts (table libraries, world interface, ABI)",
|
18
|
+
|
19
|
+
builder(yargs) {
|
20
|
+
return yargs.options({
|
21
|
+
configPath: { type: "string", desc: "Path to the config file" },
|
22
|
+
profile: { type: "string", desc: "The foundry profile to use" },
|
23
|
+
});
|
24
|
+
},
|
25
|
+
|
26
|
+
async handler({ configPath, profile }) {
|
27
|
+
const config = (await loadConfig(configPath)) as StoreConfig & WorldConfig;
|
28
|
+
const srcDir = await getSrcDirectory();
|
29
|
+
|
30
|
+
await build({ config, srcDir, foundryProfile: profile });
|
31
|
+
|
32
|
+
process.exit(0);
|
33
|
+
},
|
34
|
+
};
|
35
|
+
|
36
|
+
export default commandModule;
|
@@ -17,6 +17,7 @@ const devOptions = {
|
|
17
17
|
rpc: deployOptions.rpc,
|
18
18
|
configPath: deployOptions.configPath,
|
19
19
|
alwaysRunPostDeploy: deployOptions.alwaysRunPostDeploy,
|
20
|
+
worldAddress: deployOptions.worldAddress,
|
20
21
|
};
|
21
22
|
|
22
23
|
const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof devOptions>> = {
|
@@ -67,7 +68,7 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
|
|
67
68
|
}
|
68
69
|
});
|
69
70
|
|
70
|
-
let worldAddress
|
71
|
+
let worldAddress = opts.worldAddress as Address | undefined;
|
71
72
|
|
72
73
|
const deploys$ = lastChange$.pipe(
|
73
74
|
// debounce so that a large batch of file changes only triggers a deploy after it settles down, rather than the first change it sees (and then redeploying immediately after)
|
package/src/commands/index.ts
CHANGED
@@ -3,6 +3,7 @@ import { CommandModule } from "yargs";
|
|
3
3
|
import gasReport from "@latticexyz/gas-report";
|
4
4
|
import abiTs from "@latticexyz/abi-ts";
|
5
5
|
|
6
|
+
import build from "./build";
|
6
7
|
import devnode from "./devnode";
|
7
8
|
import faucet from "./faucet";
|
8
9
|
import hello from "./hello";
|
@@ -16,6 +17,7 @@ import devContracts from "./dev-contracts";
|
|
16
17
|
|
17
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each command has different options
|
18
19
|
export const commands: CommandModule<any, any>[] = [
|
20
|
+
build,
|
19
21
|
deploy,
|
20
22
|
devnode,
|
21
23
|
faucet,
|
@@ -56,8 +56,11 @@ const commandModule: CommandModule<Options, Options> = {
|
|
56
56
|
throw new MUDError(`These options are mutually exclusive: ${mutuallyExclusiveOptions.join(", ")}`);
|
57
57
|
}
|
58
58
|
|
59
|
+
// If the --link flag is not set, we call resolveVersion to get the version
|
59
60
|
// Resolve the version number from available options like `tag` or `commit`
|
60
|
-
|
61
|
+
if (!options.link) {
|
62
|
+
options.mudVersion = await resolveVersion(options);
|
63
|
+
}
|
61
64
|
|
62
65
|
// Update all package.json below the current working directory (except in node_modules)
|
63
66
|
const packageJsons = glob.sync("**/package.json").filter((p) => !p.includes("node_modules"));
|
package/src/commands/trace.ts
CHANGED
@@ -8,7 +8,7 @@ import { cast, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
|
|
8
8
|
import { StoreConfig } from "@latticexyz/store";
|
9
9
|
import { resolveWorldConfig, WorldConfig } from "@latticexyz/world";
|
10
10
|
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json" assert { type: "json" };
|
11
|
-
import worldConfig from "@latticexyz/world/mud.config
|
11
|
+
import worldConfig from "@latticexyz/world/mud.config";
|
12
12
|
import { resourceToHex } from "@latticexyz/common";
|
13
13
|
import { getExistingContracts } from "../utils/getExistingContracts";
|
14
14
|
import { createClient, http } from "viem";
|
package/src/debug.ts
CHANGED
@@ -1,3 +1,10 @@
|
|
1
1
|
import createDebug from "debug";
|
2
2
|
|
3
3
|
export const debug = createDebug("mud:cli");
|
4
|
+
export const error = createDebug("mud:cli");
|
5
|
+
|
6
|
+
// Pipe debug output to stdout instead of stderr
|
7
|
+
debug.log = console.debug.bind(console);
|
8
|
+
|
9
|
+
// Pipe error output to stderr
|
10
|
+
error.log = console.error.bind(console);
|
package/src/deploy/common.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Abi, Address, Hex, padHex } from "viem";
|
2
|
-
import storeConfig from "@latticexyz/store/mud.config
|
3
|
-
import worldConfig from "@latticexyz/world/mud.config
|
2
|
+
import storeConfig from "@latticexyz/store/mud.config";
|
3
|
+
import worldConfig from "@latticexyz/world/mud.config";
|
4
4
|
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json" assert { type: "json" };
|
5
5
|
import IModuleAbi from "@latticexyz/world-modules/out/IModule.sol/IModule.abi.json" assert { type: "json" };
|
6
6
|
import { Tables, configToTables } from "./configToTables";
|
@@ -9,6 +9,9 @@ import { WorldConfig, helloWorldEvent } from "@latticexyz/world";
|
|
9
9
|
|
10
10
|
export const salt = padHex("0x", { size: 32 });
|
11
11
|
|
12
|
+
// https://eips.ethereum.org/EIPS/eip-170
|
13
|
+
export const contractSizeLimit = parseInt("6000", 16);
|
14
|
+
|
12
15
|
// TODO: add `as const` to mud config so these get more strongly typed (blocked by current config parsing not using readonly)
|
13
16
|
export const storeTables = configToTables(storeConfig);
|
14
17
|
export const worldTables = configToTables(worldConfig);
|
@@ -46,6 +49,7 @@ export type WorldFunction = {
|
|
46
49
|
export type DeterministicContract = {
|
47
50
|
readonly address: Address;
|
48
51
|
readonly bytecode: Hex;
|
52
|
+
readonly deployedBytecodeSize: number;
|
49
53
|
readonly abi: Abi;
|
50
54
|
};
|
51
55
|
|
package/src/deploy/debug.ts
CHANGED
@@ -1,3 +1,10 @@
|
|
1
1
|
import { debug as parentDebug } from "../debug";
|
2
2
|
|
3
3
|
export const debug = parentDebug.extend("deploy");
|
4
|
+
export const error = parentDebug.extend("deploy");
|
5
|
+
|
6
|
+
// Pipe debug output to stdout instead of stderr
|
7
|
+
debug.log = console.debug.bind(console);
|
8
|
+
|
9
|
+
// Pipe error output to stderr
|
10
|
+
error.log = console.error.bind(console);
|
package/src/deploy/deploy.ts
CHANGED
@@ -12,10 +12,9 @@ import { Table } from "./configToTables";
|
|
12
12
|
import { assertNamespaceOwner } from "./assertNamespaceOwner";
|
13
13
|
import { debug } from "./debug";
|
14
14
|
import { resourceLabel } from "./resourceLabel";
|
15
|
-
import { ensureContract } from "./ensureContract";
|
16
15
|
import { uniqueBy } from "@latticexyz/common/utils";
|
17
16
|
import { ensureContractsDeployed } from "./ensureContractsDeployed";
|
18
|
-
import { coreModuleBytecode, worldFactoryBytecode } from "./ensureWorldFactory";
|
17
|
+
import { coreModuleBytecode, worldFactoryBytecode, worldFactoryContracts } from "./ensureWorldFactory";
|
19
18
|
|
20
19
|
type DeployOptions<configInput extends ConfigInput> = {
|
21
20
|
client: Client<Transport, Chain | undefined, Account>;
|
@@ -43,14 +42,15 @@ export async function deploy<configInput extends ConfigInput>({
|
|
43
42
|
await ensureContractsDeployed({
|
44
43
|
client,
|
45
44
|
contracts: [
|
46
|
-
|
47
|
-
{ bytecode: worldFactoryBytecode, label: "world factory" },
|
45
|
+
...worldFactoryContracts,
|
48
46
|
...uniqueBy(systems, (system) => getAddress(system.address)).map((system) => ({
|
49
47
|
bytecode: system.bytecode,
|
48
|
+
deployedBytecodeSize: system.deployedBytecodeSize,
|
50
49
|
label: `${resourceLabel(system)} system`,
|
51
50
|
})),
|
52
51
|
...uniqueBy(config.modules, (mod) => getAddress(mod.address)).map((mod) => ({
|
53
52
|
bytecode: mod.bytecode,
|
53
|
+
deployedBytecodeSize: mod.deployedBytecodeSize,
|
54
54
|
label: `${mod.name} module`,
|
55
55
|
})),
|
56
56
|
],
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import { Client, Transport, Chain, Account, concatHex, getCreate2Address, Hex } from "viem";
|
1
|
+
import { Client, Transport, Chain, Account, concatHex, getCreate2Address, Hex, size } from "viem";
|
2
2
|
import { getBytecode } from "viem/actions";
|
3
3
|
import { deployer } from "./ensureDeployer";
|
4
|
-
import { salt } from "./common";
|
4
|
+
import { contractSizeLimit, salt } from "./common";
|
5
5
|
import { sendTransaction } from "@latticexyz/common";
|
6
6
|
import { debug } from "./debug";
|
7
7
|
import pRetry from "p-retry";
|
@@ -9,12 +9,14 @@ import { wait } from "@latticexyz/common/utils";
|
|
9
9
|
|
10
10
|
export type Contract = {
|
11
11
|
bytecode: Hex;
|
12
|
+
deployedBytecodeSize: number;
|
12
13
|
label?: string;
|
13
14
|
};
|
14
15
|
|
15
16
|
export async function ensureContract({
|
16
17
|
client,
|
17
18
|
bytecode,
|
19
|
+
deployedBytecodeSize,
|
18
20
|
label = "contract",
|
19
21
|
}: {
|
20
22
|
readonly client: Client<Transport, Chain | undefined, Account>;
|
@@ -27,6 +29,16 @@ export async function ensureContract({
|
|
27
29
|
return [];
|
28
30
|
}
|
29
31
|
|
32
|
+
if (deployedBytecodeSize > contractSizeLimit) {
|
33
|
+
console.warn(
|
34
|
+
`\nBytecode for ${label} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`
|
35
|
+
);
|
36
|
+
} else if (deployedBytecodeSize > contractSizeLimit * 0.95) {
|
37
|
+
console.warn(
|
38
|
+
`\nBytecode for ${label} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`
|
39
|
+
);
|
40
|
+
}
|
41
|
+
|
30
42
|
debug("deploying", label, "at", address);
|
31
43
|
return [
|
32
44
|
await pRetry(
|
@@ -131,6 +131,7 @@ export async function ensureSystems({
|
|
131
131
|
client,
|
132
132
|
contracts: uniqueBy(missingSystems, (system) => getAddress(system.address)).map((system) => ({
|
133
133
|
bytecode: system.bytecode,
|
134
|
+
deployedBytecodeSize: system.deployedBytecodeSize,
|
134
135
|
label: `${resourceLabel(system)} system`,
|
135
136
|
})),
|
136
137
|
});
|
@@ -1,10 +1,12 @@
|
|
1
1
|
import coreModuleBuild from "@latticexyz/world/out/CoreModule.sol/CoreModule.json" assert { type: "json" };
|
2
2
|
import worldFactoryBuild from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.json" assert { type: "json" };
|
3
|
-
import { Client, Transport, Chain, Account, Hex, parseAbi, getCreate2Address, encodeDeployData } from "viem";
|
3
|
+
import { Client, Transport, Chain, Account, Hex, parseAbi, getCreate2Address, encodeDeployData, size } from "viem";
|
4
4
|
import { deployer } from "./ensureDeployer";
|
5
5
|
import { salt } from "./common";
|
6
6
|
import { ensureContractsDeployed } from "./ensureContractsDeployed";
|
7
|
+
import { Contract } from "./ensureContract";
|
7
8
|
|
9
|
+
export const coreModuleDeployedBytecodeSize = size(coreModuleBuild.deployedBytecode.object as Hex);
|
8
10
|
export const coreModuleBytecode = encodeDeployData({
|
9
11
|
bytecode: coreModuleBuild.bytecode.object as Hex,
|
10
12
|
abi: [],
|
@@ -12,6 +14,7 @@ export const coreModuleBytecode = encodeDeployData({
|
|
12
14
|
|
13
15
|
export const coreModule = getCreate2Address({ from: deployer, bytecode: coreModuleBytecode, salt });
|
14
16
|
|
17
|
+
export const worldFactoryDeployedBytecodeSize = size(worldFactoryBuild.deployedBytecode.object as Hex);
|
15
18
|
export const worldFactoryBytecode = encodeDeployData({
|
16
19
|
bytecode: worldFactoryBuild.bytecode.object as Hex,
|
17
20
|
abi: parseAbi(["constructor(address)"]),
|
@@ -20,15 +23,25 @@ export const worldFactoryBytecode = encodeDeployData({
|
|
20
23
|
|
21
24
|
export const worldFactory = getCreate2Address({ from: deployer, bytecode: worldFactoryBytecode, salt });
|
22
25
|
|
26
|
+
export const worldFactoryContracts: readonly Contract[] = [
|
27
|
+
{
|
28
|
+
bytecode: coreModuleBytecode,
|
29
|
+
deployedBytecodeSize: coreModuleDeployedBytecodeSize,
|
30
|
+
label: "core module",
|
31
|
+
},
|
32
|
+
{
|
33
|
+
bytecode: worldFactoryBytecode,
|
34
|
+
deployedBytecodeSize: worldFactoryDeployedBytecodeSize,
|
35
|
+
label: "world factory",
|
36
|
+
},
|
37
|
+
];
|
38
|
+
|
23
39
|
export async function ensureWorldFactory(
|
24
40
|
client: Client<Transport, Chain | undefined, Account>
|
25
41
|
): Promise<readonly Hex[]> {
|
26
42
|
// WorldFactory constructor doesn't call CoreModule, only sets its address, so we can do these in parallel since the address is deterministic
|
27
43
|
return await ensureContractsDeployed({
|
28
44
|
client,
|
29
|
-
contracts:
|
30
|
-
{ bytecode: coreModuleBytecode, label: "core module" },
|
31
|
-
{ bytecode: worldFactoryBytecode, label: "world factory" },
|
32
|
-
],
|
45
|
+
contracts: worldFactoryContracts,
|
33
46
|
});
|
34
47
|
}
|
package/src/deploy/getSystems.ts
CHANGED
@@ -14,7 +14,7 @@ export async function getSystems({
|
|
14
14
|
}: {
|
15
15
|
readonly client: Client;
|
16
16
|
readonly worldDeploy: WorldDeploy;
|
17
|
-
}): Promise<readonly Omit<System, "abi" | "bytecode">[]> {
|
17
|
+
}): Promise<readonly Omit<System, "abi" | "bytecode" | "deployedBytecodeSize">[]> {
|
18
18
|
const [resourceIds, functions, resourceAccess] = await Promise.all([
|
19
19
|
getResourceIds({ client, worldDeploy }),
|
20
20
|
getFunctions({ client, worldDeploy }),
|
@@ -75,6 +75,7 @@ export function resolveConfig<config extends ConfigInput>({
|
|
75
75
|
),
|
76
76
|
address: getCreate2Address({ from: deployer, bytecode: contractData.bytecode, salt }),
|
77
77
|
bytecode: contractData.bytecode,
|
78
|
+
deployedBytecodeSize: contractData.deployedBytecodeSize,
|
78
79
|
abi: contractData.abi,
|
79
80
|
functions: systemFunctions,
|
80
81
|
};
|
@@ -118,15 +119,10 @@ export function resolveConfig<config extends ConfigInput>({
|
|
118
119
|
),
|
119
120
|
};
|
120
121
|
|
121
|
-
const defaultModules = defaultModuleContracts.map((mod) => ({
|
122
|
-
name: mod.name,
|
123
|
-
bytecode: (typeof mod.bytecode === "string" ? mod.bytecode : mod.bytecode.object) as Hex,
|
124
|
-
abi: mod.abi as Abi,
|
125
|
-
}));
|
126
|
-
|
127
122
|
const modules = config.modules.map((mod) => {
|
128
123
|
const contractData =
|
129
|
-
|
124
|
+
defaultModuleContracts.find((defaultMod) => defaultMod.name === mod.name) ??
|
125
|
+
getContractData(mod.name, forgeOutDir);
|
130
126
|
const installArgs = mod.args
|
131
127
|
.map((arg) => resolveWithContext(arg, resolveContext))
|
132
128
|
.map((arg) => {
|
@@ -142,6 +138,7 @@ export function resolveConfig<config extends ConfigInput>({
|
|
142
138
|
installData: installArgs.length === 0 ? "0x" : installArgs[0],
|
143
139
|
address: getCreate2Address({ from: deployer, bytecode: contractData.bytecode, salt }),
|
144
140
|
bytecode: contractData.bytecode,
|
141
|
+
deployedBytecodeSize: contractData.deployedBytecodeSize,
|
145
142
|
abi: contractData.abi,
|
146
143
|
};
|
147
144
|
});
|
package/src/runDeploy.ts
CHANGED
@@ -7,17 +7,14 @@ import { privateKeyToAccount } from "viem/accounts";
|
|
7
7
|
import { loadConfig } from "@latticexyz/config/node";
|
8
8
|
import { StoreConfig } from "@latticexyz/store";
|
9
9
|
import { WorldConfig } from "@latticexyz/world";
|
10
|
-
import {
|
10
|
+
import { getOutDirectory, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
|
11
11
|
import chalk from "chalk";
|
12
|
-
import { execa } from "execa";
|
13
12
|
import { MUDError } from "@latticexyz/common/errors";
|
14
13
|
import { resolveConfig } from "./deploy/resolveConfig";
|
15
14
|
import { getChainId } from "viem/actions";
|
16
15
|
import { postDeploy } from "./utils/utils/postDeploy";
|
17
16
|
import { WorldDeploy } from "./deploy/common";
|
18
|
-
import {
|
19
|
-
import { worldgen } from "@latticexyz/world/node";
|
20
|
-
import { getExistingContracts } from "./utils/getExistingContracts";
|
17
|
+
import { build } from "./build";
|
21
18
|
|
22
19
|
export const deployOptions = {
|
23
20
|
configPath: { type: "string", desc: "Path to the config file" },
|
@@ -50,7 +47,6 @@ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
|
|
50
47
|
|
51
48
|
const srcDir = opts.srcDir ?? (await getSrcDirectory(profile));
|
52
49
|
const outDir = await getOutDirectory(profile);
|
53
|
-
const remappings = await getRemappings();
|
54
50
|
|
55
51
|
const rpc = opts.rpc ?? (await getRpcUrl(profile));
|
56
52
|
console.log(
|
@@ -61,10 +57,7 @@ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
|
|
61
57
|
|
62
58
|
// Run build
|
63
59
|
if (!opts.skipBuild) {
|
64
|
-
|
65
|
-
await Promise.all([tablegen(config, outPath, remappings), worldgen(config, getExistingContracts(srcDir), outPath)]);
|
66
|
-
await forge(["build"], { profile });
|
67
|
-
await execa("mud", ["abi-ts"], { stdio: "inherit" });
|
60
|
+
await build({ config, srcDir, foundryProfile: profile });
|
68
61
|
}
|
69
62
|
|
70
63
|
const privateKey = process.env.PRIVATE_KEY as Hex;
|
@@ -1,22 +1,26 @@
|
|
1
1
|
import KeysWithValueModuleData from "@latticexyz/world-modules/out/KeysWithValueModule.sol/KeysWithValueModule.json" assert { type: "json" };
|
2
2
|
import KeysInTableModuleData from "@latticexyz/world-modules/out/KeysInTableModule.sol/KeysInTableModule.json" assert { type: "json" };
|
3
3
|
import UniqueEntityModuleData from "@latticexyz/world-modules/out/UniqueEntityModule.sol/UniqueEntityModule.json" assert { type: "json" };
|
4
|
+
import { Abi, Hex, size } from "viem";
|
4
5
|
|
5
6
|
// These modules are always deployed
|
6
7
|
export const defaultModuleContracts = [
|
7
8
|
{
|
8
9
|
name: "KeysWithValueModule",
|
9
|
-
abi: KeysWithValueModuleData.abi,
|
10
|
-
bytecode: KeysWithValueModuleData.bytecode,
|
10
|
+
abi: KeysWithValueModuleData.abi as Abi,
|
11
|
+
bytecode: KeysWithValueModuleData.bytecode.object as Hex,
|
12
|
+
deployedBytecodeSize: size(KeysWithValueModuleData.deployedBytecode.object as Hex),
|
11
13
|
},
|
12
14
|
{
|
13
15
|
name: "KeysInTableModule",
|
14
|
-
abi: KeysInTableModuleData.abi,
|
15
|
-
bytecode: KeysInTableModuleData.bytecode,
|
16
|
+
abi: KeysInTableModuleData.abi as Abi,
|
17
|
+
bytecode: KeysInTableModuleData.bytecode.object as Hex,
|
18
|
+
deployedBytecodeSize: size(KeysInTableModuleData.deployedBytecode.object as Hex),
|
16
19
|
},
|
17
20
|
{
|
18
21
|
name: "UniqueEntityModule",
|
19
|
-
abi: UniqueEntityModuleData.abi,
|
20
|
-
bytecode: UniqueEntityModuleData.bytecode,
|
22
|
+
abi: UniqueEntityModuleData.abi as Abi,
|
23
|
+
bytecode: UniqueEntityModuleData.bytecode.object as Hex,
|
24
|
+
deployedBytecodeSize: size(UniqueEntityModuleData.deployedBytecode.object as Hex),
|
21
25
|
},
|
22
26
|
];
|
@@ -1,13 +1,16 @@
|
|
1
1
|
import { readFileSync } from "fs";
|
2
2
|
import path from "path";
|
3
3
|
import { MUDError } from "@latticexyz/common/errors";
|
4
|
-
import { Abi, Hex } from "viem";
|
4
|
+
import { Abi, Hex, size } from "viem";
|
5
5
|
|
6
6
|
/**
|
7
7
|
* Load the contract's abi and bytecode from the file system
|
8
8
|
* @param contractName: Name of the contract to load
|
9
9
|
*/
|
10
|
-
export function getContractData(
|
10
|
+
export function getContractData(
|
11
|
+
contractName: string,
|
12
|
+
forgeOutDirectory: string
|
13
|
+
): { bytecode: Hex; abi: Abi; deployedBytecodeSize: number } {
|
11
14
|
let data: any;
|
12
15
|
const contractDataPath = path.join(forgeOutDirectory, contractName + ".sol", contractName + ".json");
|
13
16
|
try {
|
@@ -19,8 +22,11 @@ export function getContractData(contractName: string, forgeOutDirectory: string)
|
|
19
22
|
const bytecode = data?.bytecode?.object;
|
20
23
|
if (!bytecode) throw new MUDError(`No bytecode found in ${contractDataPath}`);
|
21
24
|
|
25
|
+
const deployedBytecode = data?.deployedBytecode?.object;
|
26
|
+
if (!deployedBytecode) throw new MUDError(`No deployed bytecode found in ${contractDataPath}`);
|
27
|
+
|
22
28
|
const abi = data?.abi;
|
23
29
|
if (!abi) throw new MUDError(`No ABI found in ${contractDataPath}`);
|
24
30
|
|
25
|
-
return { abi, bytecode };
|
31
|
+
return { abi, bytecode, deployedBytecodeSize: size(deployedBytecode as Hex) };
|
26
32
|
}
|
@@ -1,23 +0,0 @@
|
|
1
|
-
import{a as H}from"./chunk-22IIKR4S.js";import Rn from"@latticexyz/gas-report";import $n from"@latticexyz/abi-ts";import{rmSync as go}from"fs";import{homedir as bo}from"os";import ho from"path";import{execa as wo}from"execa";var xo={command:"devnode",describe:"Start a local Ethereum node for development",builder(e){return e.options({blocktime:{type:"number",default:1,decs:"Interval in which new blocks are produced"}})},async handler({blocktime:e}){console.log("Clearing devnode history");let o=bo();go(ho.join(o,".foundry","anvil","tmp"),{recursive:!0,force:!0});let t=["-b",String(e),"--block-base-fee-per-gas","0"];console.log(`Running: anvil ${t.join(" ")}`);let r=wo("anvil",t,{stdio:["inherit","inherit","inherit"]});process.on("SIGINT",()=>{console.log(`
|
2
|
-
gracefully shutting down from SIGINT (Crtl-C)`),r.kill(),process.exit()}),await r}},se=xo;import{FaucetServiceDefinition as Co}from"@latticexyz/services/faucet";import{createChannel as So,createClient as vo}from"nice-grpc-web";import ae from"chalk";import{NodeHttpTransport as Do}from"@improbable-eng/grpc-web-node-http-transport";function To(e){return vo(Co,So(e,Do()))}var ko={command:"faucet",describe:"Interact with a MUD faucet",builder(e){return e.options({dripDev:{type:"boolean",desc:"Request a drip from the dev endpoint (requires faucet to have dev mode enabled)",default:!0},faucetUrl:{type:"string",desc:"URL of the MUD faucet",default:"https://faucet.testnet-mud-services.linfra.xyz"},address:{type:"string",desc:"Ethereum address to fund",required:!0}})},async handler({dripDev:e,faucetUrl:o,address:t}){let r=To(o);e&&(console.log(ae.yellow("Dripping to",t)),await r.dripDev({address:t}),console.log(ae.yellow("Success"))),process.exit(0)}},ie=ko;var Ao={command:"hello <name>",describe:"Greet <name> with Hello",builder(e){return e.options({upper:{type:"boolean"}}).positional("name",{type:"string",demandOption:!0})},handler({name:e}){let o=`Gm, ${e}!`;console.log(o),process.exit(0)}},ce=Ao;import Io from"path";import{loadConfig as Oo}from"@latticexyz/config/node";import{tablegen as Po}from"@latticexyz/store/codegen";import{getRemappings as Mo,getSrcDirectory as Wo}from"@latticexyz/common/foundry";var Fo={command:"tablegen",describe:"Autogenerate MUD Store table libraries based on the config file",builder(e){return e.options({configPath:{type:"string",desc:"Path to the config file"}})},async handler({configPath:e}){let o=await Oo(e),t=await Wo(),r=await Mo();await Po(o,Io.join(t,o.codegenDirectory),r),process.exit(0)}},de=Fo;import G from"node:path";import{existsSync as br,mkdirSync as hr,readFileSync as wr,writeFileSync as oe}from"node:fs";import{getAddress as Ue}from"viem";import{getBytecode as $o,sendRawTransaction as Eo,sendTransaction as No,waitForTransactionReceipt as me}from"viem/actions";var F={gasPrice:1e11,gasLimit:1e5,signerAddress:"3fab184622dc19b6109349b94811493bf2a45362",transaction:"f8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222",address:"4e59b44847b379578588920ca78fbf26c0b4956c"};import Ro from"debug";var le=Ro("mud:cli");var l=le.extend("deploy");var v=`0x${F.address}`;async function pe(e){if(await $o(e,{address:v})){l("found create2 deployer at",v);return}l("sending gas for create2 deployer to signer at",F.signerAddress);let t=await No(e,{chain:e.chain??null,to:`0x${F.signerAddress}`,value:BigInt(F.gasLimit)*BigInt(F.gasPrice)}),r=await me(e,{hash:t});if(r.status!=="success")throw console.error("failed to send gas to deployer signer",r),new Error("failed to send gas to deployer signer");l("deploying create2 deployer at",v);let n=await Eo(e,{serializedTransaction:`0x${F.transaction}`}),s=await me(e,{hash:n});if(s.contractAddress!==v)throw console.error("unexpected contract address for deployer",s),new Error("unexpected contract address for deployer")}import{waitForTransactionReceipt as ct}from"viem/actions";import et from"@latticexyz/world/out/CoreModule.sol/CoreModule.json"assert{type:"json"};import ot from"@latticexyz/world/out/WorldFactory.sol/WorldFactory.json"assert{type:"json"};import{parseAbi as tt,getCreate2Address as be,encodeDeployData as he}from"viem";import{padHex as Ho}from"viem";import Vo from"@latticexyz/store/mud.config.js";import Uo from"@latticexyz/world/mud.config.js";import _o from"@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json"assert{type:"json"};import Lo from"@latticexyz/world-modules/out/IModule.sol/IModule.abi.json"assert{type:"json"};import{resourceToHex as Bo}from"@latticexyz/common";import{resolveUserTypes as fe}from"@latticexyz/store";function N(e){let o={...e.userTypes,...Object.fromEntries(Object.entries(e.enums).map(([t])=>[t,{internalType:"uint8"}]))};return Object.fromEntries(Object.entries(e.tables).map(([t,r])=>[`${e.namespace}_${t}`,{namespace:e.namespace,name:r.name,tableId:Bo({type:r.offchainOnly?"offchainTable":"table",namespace:e.namespace,name:r.name}),keySchema:fe(r.keySchema,o),valueSchema:fe(r.valueSchema,o)}]))}import{helloStoreEvent as Ko}from"@latticexyz/store";import{helloWorldEvent as zo}from"@latticexyz/world";var k=Ho("0x",{size:32}),j=N(Vo),D=N(Uo),V=[Ko,zo],C=[..._o,...Lo],ue=["1.0.0-unaudited"],ye=["1.0.0-unaudited"];import{waitForTransactionReceipt as Xo}from"viem/actions";import{concatHex as Jo,getCreate2Address as qo}from"viem";import{getBytecode as Go}from"viem/actions";import{sendTransaction as Yo}from"@latticexyz/common";import Zo from"p-retry";import{wait as Qo}from"@latticexyz/common/utils";async function ge({client:e,bytecode:o,label:t="contract"}){let r=qo({from:v,salt:k,bytecode:o});return await Go(e,{address:r,blockTag:"pending"})?(l("found",t,"at",r),[]):(l("deploying",t,"at",r),[await Zo(()=>Yo(e,{chain:e.chain??null,to:v,data:Jo([k,o])}),{retries:3,onFailedAttempt:async s=>{let d=s.attemptNumber*500;l(`failed to deploy ${t}, retrying in ${d}ms...`),await Qo(d)}})])}async function I({client:e,contracts:o}){let t=(await Promise.all(o.map(r=>ge({client:e,...r})))).flat();if(t.length){l("waiting for contracts");for(let r of t)await Xo(e,{hash:r})}return t}var U=he({bytecode:et.bytecode.object,abi:[]}),rt=be({from:v,bytecode:U,salt:k}),_=he({bytecode:ot.bytecode.object,abi:tt(["constructor(address)"]),args:[rt]}),we=be({from:v,bytecode:_,salt:k});async function xe(e){return await I({client:e,contracts:[{bytecode:U,label:"core module"},{bytecode:_,label:"world factory"}]})}import dt from"@latticexyz/world/out/WorldFactory.sol/WorldFactory.abi.json"assert{type:"json"};import{writeContract as lt}from"@latticexyz/common";import{AbiEventSignatureNotFoundError as nt,decodeEventLog as st,hexToString as Ce,parseAbi as at,trim as Se}from"viem";import{isDefined as it}from"@latticexyz/common/utils";function L(e){let o=e.map(d=>{try{return{...d,...st({strict:!0,abi:at(V),topics:d.topics,data:d.data})}}catch(a){if(a instanceof nt)return;throw a}}).filter(it),{address:t,deployBlock:r,worldVersion:n,storeVersion:s}=o.reduce((d,a)=>({...d,address:a.address,deployBlock:a.blockNumber,...a.eventName==="HelloWorld"?{worldVersion:Ce(Se(a.args.worldVersion,{dir:"right"}))}:null,...a.eventName==="HelloStore"?{storeVersion:Ce(Se(a.args.storeVersion,{dir:"right"}))}:null}),{});if(t==null)throw new Error("could not find world address");if(r==null)throw new Error("could not find world deploy block number");if(n==null)throw new Error("could not find world version");if(s==null)throw new Error("could not find store version");return{address:t,deployBlock:r,worldVersion:n,storeVersion:s}}async function ve(e){await xe(e),l("deploying world");let o=await lt(e,{chain:e.chain??null,address:we,abi:dt,functionName:"deployWorld"});l("waiting for world deploy");let t=await ct(e,{hash:o});if(t.status!=="success")throw console.error("world deploy failed",t),new Error("world deploy failed");let r=L(t.logs.map(n=>n));return l("deployed world to",r.address,"at block",r.deployBlock),{...r,stateBlock:r.deployBlock}}import{writeContract as bt}from"@latticexyz/common";import{valueSchemaToFieldLayoutHex as ht,keySchemaToHex as wt,valueSchemaToHex as xt}from"@latticexyz/protocol-parser";function x({namespace:e,name:o}){return`${e}:${o}`}import{parseAbiItem as mt,decodeAbiParameters as De,parseAbiParameters as Te}from"viem";import{hexToResource as pt}from"@latticexyz/common";import{storeSetRecordEvent as ft}from"@latticexyz/store";import{getLogs as ut}from"viem/actions";import{decodeKey as yt,decodeValueArgs as gt,hexToSchema as ke}from"@latticexyz/protocol-parser";async function Ae({client:e,worldDeploy:o}){l("looking up tables for",o.address);let r=(await ut(e,{strict:!0,fromBlock:o.deployBlock,toBlock:o.stateBlock,address:o.address,event:mt(ft),args:{tableId:j.store_Tables.tableId}})).map(n=>{let{tableId:s}=yt(j.store_Tables.keySchema,n.args.keyTuple),{namespace:d,name:a}=pt(s),p=gt(j.store_Tables.valueSchema,n.args),i=ke(p.keySchema),m=ke(p.valueSchema),u=De(Te("string[]"),p.abiEncodedKeyNames)[0],S=De(Te("string[]"),p.abiEncodedFieldNames)[0],f=[...m.staticFields,...m.dynamicFields],g=Object.fromEntries(i.staticFields.map((b,c)=>[u[c],b])),h=Object.fromEntries(f.map((b,c)=>[S[c],b]));return{namespace:d,name:a,tableId:s,keySchema:g,valueSchema:h}});return l("found",r.length,"tables for",o.address),r}import Ct from"p-retry";import{wait as St}from"@latticexyz/common/utils";async function Ie({client:e,worldDeploy:o,tables:t}){let n=(await Ae({client:e,worldDeploy:o})).map(a=>a.tableId),s=t.filter(a=>n.includes(a.tableId));s.length&&l("existing tables",s.map(x).join(", "));let d=t.filter(a=>!n.includes(a.tableId));return d.length?(l("registering tables",d.map(x).join(", ")),await Promise.all(d.map(a=>Ct(()=>bt(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"registerTable",args:[a.tableId,ht(a.valueSchema),wt(a.keySchema),xt(a.valueSchema),Object.keys(a.keySchema),Object.keys(a.valueSchema)]}),{retries:3,onFailedAttempt:async p=>{let i=p.attemptNumber*500;l(`failed to register table ${x(a)}, retrying in ${i}ms...`),await St(i)}})))):[]}import{getAddress as A}from"viem";import{writeContract as Y}from"@latticexyz/common";import{parseAbiItem as vt}from"viem";import{getLogs as Dt}from"viem/actions";import{storeSpliceStaticDataEvent as Tt}from"@latticexyz/store";async function K({client:e,worldDeploy:o}){l("looking up resource IDs for",o.address);let r=(await Dt(e,{strict:!0,address:o.address,fromBlock:o.deployBlock,toBlock:o.stateBlock,event:vt(Tt),args:{tableId:j.store_ResourceIds.tableId}})).map(n=>n.args.keyTuple[0]);return l("found",r.length,"resource IDs for",o.address),r}import{hexToResource as Ht}from"@latticexyz/common";import{decodeValueArgs as kt,encodeKey as At}from"@latticexyz/protocol-parser";import{readContract as It}from"viem/actions";async function O({client:e,worldDeploy:o,table:t,key:r}){let[n,s,d]=await It(e,{blockNumber:o.stateBlock,address:o.address,abi:C,functionName:"getRecord",args:[t.tableId,At(t.keySchema,r)]});return kt(t.valueSchema,{staticData:n,encodedLengths:s,dynamicData:d})}import{getFunctionSelector as Ot,parseAbiItem as Pt}from"viem";import{storeSetRecordEvent as Mt}from"@latticexyz/store";import{getLogs as Wt}from"viem/actions";import{decodeValueArgs as Ft}from"@latticexyz/protocol-parser";import{hexToResource as jt}from"@latticexyz/common";async function z({client:e,worldDeploy:o}){l("looking up function signatures for",o.address);let r=(await Wt(e,{strict:!0,fromBlock:o.deployBlock,toBlock:o.stateBlock,address:o.address,event:Pt(Mt),args:{tableId:D.world_FunctionSignatures.tableId}})).map(s=>Ft(D.world_FunctionSignatures.valueSchema,s.args).functionSignature);return l("found",r.length,"function signatures for",o.address),await Promise.all(r.map(async s=>{let d=Ot(s),{systemId:a,systemFunctionSelector:p}=await O({client:e,worldDeploy:o,table:D.world_FunctionSelectors,key:{functionSelector:d}}),{namespace:i,name:m}=jt(a),u=i===""?s:s.replace(`${i}_${m}_`,"");return{signature:s,selector:d,systemId:a,systemFunctionSignature:u,systemFunctionSelector:p}}))}import{parseAbiItem as Rt,getAddress as $t}from"viem";import{storeSpliceStaticDataEvent as Et}from"@latticexyz/store";import{getLogs as Nt}from"viem/actions";import{decodeKey as Bt}from"@latticexyz/protocol-parser";async function J({client:e,worldDeploy:o}){l("looking up resource access for",o.address);let r=(await Nt(e,{strict:!0,fromBlock:o.deployBlock,toBlock:o.stateBlock,address:o.address,event:Rt(Et),args:{tableId:D.world_ResourceAccess.tableId}})).map(s=>Bt(D.world_ResourceAccess.keySchema,s.args.keyTuple)),n=(await Promise.all(r.map(async s=>[s,await O({client:e,worldDeploy:o,table:D.world_ResourceAccess,key:s})]))).filter(([,s])=>s.access).map(([s])=>({resourceId:s.resourceId,address:$t(s.caller)}));return l("found",n.length,"resource<>address access pairs"),n}async function Oe({client:e,worldDeploy:o}){let[t,r,n]=await Promise.all([K({client:e,worldDeploy:o}),z({client:e,worldDeploy:o}),J({client:e,worldDeploy:o})]),s=t.map(Ht).filter(d=>d.type==="system");return l("looking up systems",s.map(x).join(", ")),await Promise.all(s.map(async d=>{let{system:a,publicAccess:p}=await O({client:e,worldDeploy:o,table:D.world_Systems,key:{systemId:d.resourceId}}),i=r.filter(m=>m.systemId===d.resourceId);return{address:a,namespace:d.namespace,name:d.name,systemId:d.resourceId,allowAll:p,allowedAddresses:n.filter(({resourceId:m})=>m===d.resourceId).map(({address:m})=>m),functions:i}}))}import{uniqueBy as Vt,wait as Z}from"@latticexyz/common/utils";import Q from"p-retry";async function Pe({client:e,worldDeploy:o,systems:t}){let[r,n]=await Promise.all([Oe({client:e,worldDeploy:o}),J({client:e,worldDeploy:o})]),s=t.map(c=>c.systemId),d=n.filter(({resourceId:c})=>s.includes(c)),a=t.flatMap(c=>c.allowedAddresses.map(y=>({resourceId:c.systemId,address:y}))),p=a.filter(c=>!d.some(({resourceId:y,address:w})=>y===c.resourceId&&A(w)===A(c.address))),i=d.filter(c=>!a.some(({resourceId:y,address:w})=>y===c.resourceId&&A(w)===A(c.address)));i.length&&l("revoking",i.length,"access grants"),p.length&&l("adding",p.length,"access grants");let m=[...i.map(c=>Q(()=>Y(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"revokeAccess",args:[c.resourceId,c.address]}),{retries:3,onFailedAttempt:async y=>{let w=y.attemptNumber*500;l(`failed to revoke access, retrying in ${w}ms...`),await Z(w)}})),...p.map(c=>Q(()=>Y(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"grantAccess",args:[c.resourceId,c.address]}),{retries:3,onFailedAttempt:async y=>{let w=y.attemptNumber*500;l(`failed to grant access, retrying in ${w}ms...`),await Z(w)}}))],u=t.filter(c=>r.some(y=>y.systemId===c.systemId&&A(y.address)===A(c.address)));u.length&&l("existing systems",u.map(x).join(", "));let S=u.map(c=>c.systemId),f=t.filter(c=>!S.includes(c.systemId));if(!f.length)return[];let g=f.filter(c=>r.some(y=>y.systemId===c.systemId&&A(y.address)!==A(c.address)));g.length&&l("upgrading systems",g.map(x).join(", "));let h=f.filter(c=>!r.some(y=>y.systemId===c.systemId));h.length&&l("registering new systems",h.map(x).join(", ")),await I({client:e,contracts:Vt(f,c=>A(c.address)).map(c=>({bytecode:c.bytecode,label:`${x(c)} system`}))});let b=f.map(c=>Q(()=>Y(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"registerSystem",args:[c.systemId,c.address,c.allowAll]}),{retries:3,onFailedAttempt:async y=>{let w=y.attemptNumber*500;l(`failed to register system ${x(c)}, retrying in ${w}ms...`),await Z(w)}}));return await Promise.all([...m,...b])}import{waitForTransactionReceipt as er}from"viem/actions";import{getAddress as Ut,parseAbi as _t}from"viem";import{getBlockNumber as Lt,getLogs as Kt}from"viem/actions";var Me=new Map;async function We(e,o){let t=Ut(o),r=Me.get(t);if(r!=null)return r;l("looking up world deploy for",t);let n=await Lt(e),s=await Kt(e,{strict:!0,address:t,events:_t(V),fromBlock:"earliest",toBlock:n});return r={...L(s),stateBlock:n},Me.set(t,r),l("found world deploy for",t,"at block",r.deployBlock),r}import{hexToResource as zt,writeContract as Fe}from"@latticexyz/common";import je from"p-retry";import{wait as Re}from"@latticexyz/common/utils";async function $e({client:e,worldDeploy:o,functions:t}){let r=await z({client:e,worldDeploy:o}),n=Object.fromEntries(r.map(a=>[a.selector,a])),s=t.filter(a=>n[a.selector]),d=t.filter(a=>!s.includes(a));if(s.length){l("functions already registered:",s.map(p=>p.signature).join(", "));let a=s.filter(p=>p.systemId!==n[p.selector]?.systemId);a.length&&console.warn("found",a.length,"functions already registered but pointing at a different system ID:",a.map(p=>p.signature).join(", "))}return d.length?(l("registering functions:",d.map(a=>a.signature).join(", ")),Promise.all(d.map(a=>{let{namespace:p}=zt(a.systemId);return p===""?je(()=>Fe(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"registerRootFunctionSelector",args:[a.systemId,a.systemFunctionSignature,a.systemFunctionSelector]}),{retries:3,onFailedAttempt:async i=>{let m=i.attemptNumber*500;l(`failed to register function ${a.signature}, retrying in ${m}ms...`),await Re(m)}}):je(()=>Fe(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"registerFunctionSelector",args:[a.systemId,a.systemFunctionSignature]}),{retries:3,onFailedAttempt:async i=>{let m=i.attemptNumber*500;l(`failed to register function ${a.signature}, retrying in ${m}ms...`),await Re(m)}})}))):[]}import{BaseError as Jt,getAddress as qt}from"viem";import{writeContract as Ee}from"@latticexyz/common";import{isDefined as Gt,uniqueBy as Yt,wait as Zt}from"@latticexyz/common/utils";import Qt from"p-retry";async function Ne({client:e,worldDeploy:o,modules:t}){return t.length?(await I({client:e,contracts:Yt(t,r=>qt(r.address)).map(r=>({bytecode:r.bytecode,label:`${r.name} module`}))}),l("installing modules:",t.map(r=>r.name).join(", ")),(await Promise.all(t.map(r=>Qt(async()=>{try{return r.installAsRoot?await Ee(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"installRootModule",args:[r.address,r.installData]}):await Ee(e,{chain:e.chain??null,address:o.address,abi:C,functionName:"installModule",args:[r.address,r.installData]})}catch(n){if(n instanceof Jt&&n.message.includes("Module_AlreadyInstalled")){l(`module ${r.name} already installed`);return}throw n}},{retries:3,onFailedAttempt:async n=>{let s=n.attemptNumber*500;l(`failed to install module ${r.name}, retrying in ${s}ms...`),await Zt(s)}})))).filter(Gt)):[]}import{getAddress as Be}from"viem";import{hexToResource as He,resourceToHex as Xt}from"@latticexyz/common";async function Ve({client:e,worldDeploy:o,resourceIds:t}){let r=Array.from(new Set(t.map(i=>He(i).namespace))),n=await K({client:e,worldDeploy:o}),s=Array.from(new Set(n.map(i=>He(i).namespace))),d=r.filter(i=>s.includes(i)),p=(await Promise.all(d.map(async i=>{let{owner:m}=await O({client:e,worldDeploy:o,table:D.world_NamespaceOwner,key:{namespaceId:Xt({type:"namespace",namespace:i,name:""})}});return[i,m]}))).filter(([,i])=>Be(i)!==Be(e.account.address)).map(([i])=>i);if(p.length)throw new Error(`You are attempting to deploy to namespaces you do not own: ${p.join(", ")}`)}import{uniqueBy as _e}from"@latticexyz/common/utils";async function Le({client:e,config:o,worldAddress:t}){let r=Object.values(o.tables),n=Object.values(o.systems);await pe(e),await I({client:e,contracts:[{bytecode:U,label:"core module"},{bytecode:_,label:"world factory"},..._e(n,u=>Ue(u.address)).map(u=>({bytecode:u.bytecode,label:`${x(u)} system`})),..._e(o.modules,u=>Ue(u.address)).map(u=>({bytecode:u.bytecode,label:`${u.name} module`}))]});let s=t?await We(e,t):await ve(e);if(!ue.includes(s.storeVersion))throw new Error(`Unsupported Store version: ${s.storeVersion}`);if(!ye.includes(s.worldVersion))throw new Error(`Unsupported World version: ${s.worldVersion}`);await Ve({client:e,worldDeploy:s,resourceIds:[...r.map(u=>u.tableId),...n.map(u=>u.systemId)]});let d=await Ie({client:e,worldDeploy:s,tables:r}),a=await Pe({client:e,worldDeploy:s,systems:n}),p=await $e({client:e,worldDeploy:s,functions:n.flatMap(u=>u.functions)}),i=await Ne({client:e,worldDeploy:s,modules:o.modules}),m=[...d,...a,...p,...i];l("waiting for all transactions to confirm");for(let u of m)await er(e,{hash:u});return l("deploy complete"),s}import{createWalletClient as xr,http as Cr}from"viem";import{privateKeyToAccount as Sr}from"viem/accounts";import{loadConfig as vr}from"@latticexyz/config/node";import{forge as Dr,getOutDirectory as Tr,getRemappings as kr,getRpcUrl as Ar,getSrcDirectory as Ir}from"@latticexyz/common/foundry";import R from"chalk";import{execa as Or}from"execa";import{MUDError as Pr}from"@latticexyz/common/errors";import{resolveWorldConfig as sr}from"@latticexyz/world";import{resourceToHex as ee,hexToResource as ar}from"@latticexyz/common";import{resolveWithContext as ir}from"@latticexyz/config";import{encodeField as cr}from"@latticexyz/protocol-parser";import{getFunctionSelector as Ge,getCreate2Address as Ye,getAddress as dr,hexToBytes as lr,bytesToHex as mr,getFunctionSignature as Ze}from"viem";import or from"glob";import{basename as tr}from"path";function P(e){return or.sync(`${e}/**/*.sol`).map(o=>({path:o,basename:tr(o,".sol")}))}import Ke from"@latticexyz/world-modules/out/KeysWithValueModule.sol/KeysWithValueModule.json"assert{type:"json"};import ze from"@latticexyz/world-modules/out/KeysInTableModule.sol/KeysInTableModule.json"assert{type:"json"};import Je from"@latticexyz/world-modules/out/UniqueEntityModule.sol/UniqueEntityModule.json"assert{type:"json"};var qe=[{name:"KeysWithValueModule",abi:Ke.abi,bytecode:Ke.bytecode},{name:"KeysInTableModule",abi:ze.abi,bytecode:ze.bytecode},{name:"UniqueEntityModule",abi:Je.abi,bytecode:Je.bytecode}];import{readFileSync as rr}from"fs";import nr from"path";import{MUDError as X}from"@latticexyz/common/errors";function q(e,o){let t,r=nr.join(o,e+".sol",e+".json");try{t=JSON.parse(rr(r,"utf8"))}catch{throw new X(`Error reading file at ${r}`)}let n=t?.bytecode?.object;if(!n)throw new X(`No bytecode found in ${r}`);let s=t?.abi;if(!s)throw new X(`No ABI found in ${r}`);return{abi:s,bytecode:n}}function Qe({config:e,forgeSourceDir:o,forgeOutDir:t}){let r=N(e),n=P(o).map(({basename:f})=>f),s=sr(e,n),a=q("System",t).abi.filter(f=>f.type==="function").map(Ze),p=Object.entries(s.systems).map(([f,g])=>{let h=e.namespace,b=g.name,c=ee({type:"system",namespace:h,name:b}),y=q(f,t),w=y.abi.filter(T=>T.type==="function").map(Ze).filter(T=>!a.includes(T)).map(T=>{let ne=h===""?T:`${h}_${b}_${T}`;return{signature:ne,selector:Ge(ne),systemId:c,systemFunctionSignature:T,systemFunctionSelector:Ge(T)}});return{namespace:h,name:b,systemId:c,allowAll:g.openAccess,allowedAddresses:g.accessListAddresses,allowedSystemIds:g.accessListSystems.map(T=>ee({type:"system",namespace:h,name:s.systems[T].name})),address:Ye({from:v,bytecode:y.bytecode,salt:k}),bytecode:y.bytecode,abi:y.abi,functions:w}}),i=p.map(({allowedAddresses:f,allowedSystemIds:g,...h})=>{let b=g.map(c=>{let y=p.find(w=>w.systemId===c);if(!y)throw new Error(`System ${x(h)} wanted access to ${x(ar(c))}, but it wasn't found in the config.`);return y.address});return{...h,allowedAddresses:Array.from(new Set([...f,...b].map(c=>dr(c))))}}),m={tableIds:Object.fromEntries(Object.entries(e.tables).map(([f,g])=>[f,lr(ee({type:g.offchainOnly?"offchainTable":"table",namespace:e.namespace,name:g.name}))]))},u=qe.map(f=>({name:f.name,bytecode:typeof f.bytecode=="string"?f.bytecode:f.bytecode.object,abi:f.abi})),S=e.modules.map(f=>{let g=u.find(b=>b.name===f.name)??q(f.name,t),h=f.args.map(b=>ir(b,m)).map(b=>{let c=b.value instanceof Uint8Array?mr(b.value):b.value;return cr(b.type,c)});if(h.length>1)throw new Error(`${f.name} module should only have 0-1 args, but had ${h.length} args.`);return{name:f.name,installAsRoot:f.root,installData:h.length===0?"0x":h[0],address:Ye({from:v,bytecode:g.bytecode,salt:k}),bytecode:g.bytecode,abi:g.abi}});return{tables:r,systems:i,modules:S}}import{getChainId as Mr}from"viem/actions";import{existsSync as pr}from"fs";import fr from"path";import ur from"chalk";import{getScriptDirectory as yr,forge as gr}from"@latticexyz/common/foundry";async function Xe(e,o,t,r){let n=fr.join(await yr(),e+".s.sol");pr(n)?(console.log(ur.blue(`Executing post deploy script at ${n}`)),await gr(["script",e,"--sig","run(address)",o,"--broadcast","--rpc-url",t,"-vvv"],{profile:r})):console.log(`No script at ${n}, skipping post deploy hook`)}import{tablegen as Wr}from"@latticexyz/store/codegen";import{worldgen as Fr}from"@latticexyz/world/node";var M={configPath:{type:"string",desc:"Path to the config file"},printConfig:{type:"boolean",desc:"Print the resolved config"},profile:{type:"string",desc:"The foundry profile to use"},saveDeployment:{type:"boolean",desc:"Save the deployment info to a file",default:!0},rpc:{type:"string",desc:"The RPC URL to use. Defaults to the RPC url from the local foundry.toml"},worldAddress:{type:"string",desc:"Deploy to an existing World at the given address"},srcDir:{type:"string",desc:"Source directory. Defaults to foundry src directory."},skipBuild:{type:"boolean",desc:"Skip rebuilding the contracts before deploying"},alwaysRunPostDeploy:{type:"boolean",desc:"Always run PostDeploy.s.sol after each deploy (including during upgrades). By default, PostDeploy.s.sol is only run once after a new world is deployed."}};async function $(e){let o=e.profile??process.env.FOUNDRY_PROFILE,t=await vr(e.configPath);e.printConfig&&console.log(R.green(`
|
3
|
-
Resolved config:
|
4
|
-
`),JSON.stringify(t,null,2));let r=e.srcDir??await Ir(o),n=await Tr(o),s=await kr(),d=e.rpc??await Ar(o);if(console.log(R.bgBlue(R.whiteBright(`
|
5
|
-
Deploying MUD contracts${o?" with profile "+o:""} to RPC ${d}
|
6
|
-
`))),!e.skipBuild){let f=G.join(r,t.codegenDirectory);await Promise.all([Wr(t,f,s),Fr(t,P(r),f)]),await Dr(["build"],{profile:o}),await Or("mud",["abi-ts"],{stdio:"inherit"})}let a=process.env.PRIVATE_KEY;if(!a)throw new Pr(`Missing PRIVATE_KEY environment variable.
|
7
|
-
Run 'echo "PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" > .env'
|
8
|
-
in your contracts directory to use the default anvil private key.`);let p=Qe({config:t,forgeSourceDir:r,forgeOutDir:n}),i=xr({transport:Cr(d),account:Sr(a)});console.log("Deploying from",i.account.address);let m=Date.now(),u=await Le({worldAddress:e.worldAddress,client:i,config:p});(e.worldAddress==null||e.alwaysRunPostDeploy)&&await Xe(t.postDeployScript,u.address,d,o),console.log(R.green("Deployment completed in",(Date.now()-m)/1e3,"seconds"));let S={worldAddress:u.address,blockNumber:Number(u.deployBlock)};if(e.saveDeployment){let f=await Mr(i),g=G.join(t.deploysDirectory,f.toString());hr(g,{recursive:!0}),oe(G.join(g,"latest.json"),JSON.stringify(S,null,2)),oe(G.join(g,Date.now()+".json"),JSON.stringify(S,null,2));let h=[1337,31337],b=br(t.worldsFile)?JSON.parse(wr(t.worldsFile,"utf-8")):{};b[f]={address:S.worldAddress,blockNumber:h.includes(f)?void 0:S.blockNumber},oe(t.worldsFile,JSON.stringify(b,null,2)),console.log(R.bgGreen(R.whiteBright(`
|
9
|
-
Deployment result (written to ${t.worldsFile} and ${g}):
|
10
|
-
`)))}return console.log(S),u}var jr={command:"deploy",describe:"Deploy MUD contracts",builder(e){return e.options(M)},async handler(e){try{await $(e)}catch(o){H(o),process.exit(1)}process.exit(0)}},eo=jr;import{loadConfig as Rr}from"@latticexyz/config/node";import{worldgen as $r}from"@latticexyz/world/node";import{getSrcDirectory as Er}from"@latticexyz/common/foundry";import oo from"path";import{rmSync as Nr}from"fs";var Br={command:"worldgen",describe:"Autogenerate interfaces for Systems and World based on existing contracts and the config file",builder(e){return e.options({configPath:{type:"string",desc:"Path to the config file"},clean:{type:"boolean",desc:"Clear the worldgen directory before generating new interfaces (defaults to true)",default:!0}})},async handler(e){await Hr(e),process.exit(0)}};async function Hr(e){let o=e.srcDir??await Er(),t=P(o),r=e.config??await Rr(e.configPath),n=oo.join(o,r.codegenDirectory);e.clean&&Nr(oo.join(n,r.worldgenDirectory),{recursive:!0,force:!0}),await $r(r,t,n)}var to=Br;import B from"chalk";import{readFileSync as Kr,writeFileSync as zr}from"fs";import re from"path";import{MUDError as E}from"@latticexyz/common/errors";var ro={name:"@latticexyz/cli",version:"2.0.0-next.13",description:"Command line interface for mud",repository:{type:"git",url:"https://github.com/latticexyz/mud.git",directory:"packages/cli"},license:"MIT",type:"module",exports:{".":"./dist/index.js"},types:"src/index.ts",bin:{mud:"./dist/mud.js"},scripts:{build:"pnpm run build:js && pnpm run build:test-tables","build:js":"tsup && chmod +x ./dist/mud.js","build:test-tables":"tsx ./scripts/generate-test-tables.ts",clean:"pnpm run clean:js && pnpm run clean:test-tables","clean:js":"rimraf dist","clean:test-tables":"rimraf src/codegen",dev:"tsup --watch",lint:"eslint . --ext .ts",prepare:"mkdir -p ./dist && touch ./dist/mud.js",test:"tsc --noEmit && forge test","test:ci":"pnpm run test"},dependencies:{"@ethersproject/abi":"^5.7.0","@ethersproject/providers":"^5.7.2","@improbable-eng/grpc-web":"^0.15.0","@improbable-eng/grpc-web-node-http-transport":"^0.15.0","@latticexyz/abi-ts":"workspace:*","@latticexyz/common":"workspace:*","@latticexyz/config":"workspace:*","@latticexyz/gas-report":"workspace:*","@latticexyz/protocol-parser":"workspace:*","@latticexyz/schema-type":"workspace:*","@latticexyz/services":"workspace:*","@latticexyz/store":"workspace:*","@latticexyz/utils":"workspace:*","@latticexyz/world":"workspace:*","@latticexyz/world-modules":"workspace:*",chalk:"^5.0.1",chokidar:"^3.5.3",debug:"^4.3.4",dotenv:"^16.0.3",ejs:"^3.1.8",ethers:"^5.7.2",execa:"^7.0.0",glob:"^8.0.3","nice-grpc-web":"^2.0.1",openurl:"^1.1.1","p-retry":"^5.1.2",path:"^0.12.7",rxjs:"7.5.5","throttle-debounce":"^5.0.0",typescript:"5.1.6",viem:"1.14.0",yargs:"^17.7.1",zod:"^3.21.4","zod-validation-error":"^1.3.0"},devDependencies:{"@types/debug":"^4.1.7","@types/ejs":"^3.1.1","@types/glob":"^7.2.0","@types/node":"^18.15.11","@types/openurl":"^1.0.0","@types/throttle-debounce":"^5.0.0","@types/yargs":"^17.0.10","ds-test":"https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0","forge-std":"https://github.com/foundry-rs/forge-std.git#74cfb77e308dd188d2f58864aaf44963ae6b88b1",tsup:"^6.7.0",tsx:"^3.12.6",vitest:"0.31.4"},gitHead:"914a1e0ae4a573d685841ca2ea921435057deb8f"};import Jr from"glob";import{ZodError as Ur,z as no}from"zod";var _r=no.object({MUD_PACKAGES:no.string().transform(e=>JSON.parse(e))});function Lr(){try{return _r.parse({MUD_PACKAGES:'{"@latticexyz/abi-ts":{"localPath":"packages/abi-ts"},"@latticexyz/block-logs-stream":{"localPath":"packages/block-logs-stream"},"@latticexyz/cli":{"localPath":"packages/cli"},"@latticexyz/common":{"localPath":"packages/common"},"@latticexyz/config":{"localPath":"packages/config"},"create-mud":{"localPath":"packages/create-mud"},"@latticexyz/dev-tools":{"localPath":"packages/dev-tools"},"@latticexyz/faucet":{"localPath":"packages/faucet"},"@latticexyz/gas-report":{"localPath":"packages/gas-report"},"@latticexyz/noise":{"localPath":"packages/noise"},"@latticexyz/phaserx":{"localPath":"packages/phaserx"},"@latticexyz/protocol-parser":{"localPath":"packages/protocol-parser"},"@latticexyz/react":{"localPath":"packages/react"},"@latticexyz/recs":{"localPath":"packages/recs"},"@latticexyz/schema-type":{"localPath":"packages/schema-type"},"@latticexyz/services":{"localPath":"packages/services"},"solhint-config-mud":{"localPath":"packages/solhint-config-mud"},"solhint-plugin-mud":{"localPath":"packages/solhint-plugin-mud"},"@latticexyz/store-indexer":{"localPath":"packages/store-indexer"},"@latticexyz/store-sync":{"localPath":"packages/store-sync"},"@latticexyz/store":{"localPath":"packages/store"},"@latticexyz/utils":{"localPath":"packages/utils"},"@latticexyz/world-modules":{"localPath":"packages/world-modules"},"@latticexyz/world":{"localPath":"packages/world"}}'})}catch(e){if(e instanceof Ur){let{_errors:o,...t}=e.format();console.error(`
|
11
|
-
Missing or invalid environment variables:
|
12
|
-
|
13
|
-
${Object.keys(t).join(`
|
14
|
-
`)}
|
15
|
-
`),process.exit(1)}throw e}}var te=Lr().MUD_PACKAGES;var qr={command:"set-version",describe:"Set MUD version in all package.json files and optionally backup the previously installed version",builder(e){return e.options({mudVersion:{alias:"v",type:"string",description:"Set MUD to the given version"},tag:{alias:"t",type:"string",description:"Set MUD to the latest version with the given tag from npm"},commit:{alias:"c",type:"string",description:"Set MUD to the version based on a given git commit hash from npm"},link:{alias:"l",type:"string",description:"Relative path to the local MUD root directory to link"}})},async handler(e){try{let o=["mudVersion","link","tag","commit","restore"],t=o.reduce((n,s)=>e[s]?n+1:n,0);if(t===0)throw new E(`You need to provide one these options: ${o.join(", ")}`);if(t>1)throw new E(`These options are mutually exclusive: ${o.join(", ")}`);e.mudVersion=await Gr(e);let r=Jr.sync("**/package.json").filter(n=>!n.includes("node_modules"));for(let n of r)Yr(n,e)}catch(o){H(o)}finally{process.exit(0)}}};async function Gr(e){e.mudVersion==="canary"&&(e.tag="main");let o;try{console.log(B.blue("Fetching available versions")),o=await(await fetch(`https://registry.npmjs.org/${ro.name}`)).json()}catch{throw new E("Could not fetch available MUD versions")}if(e.tag){let t=o["dist-tags"][e.tag];if(!t)throw new E(`Could not find npm version with tag "${e.tag}"`);return console.log(B.green(`Latest version with tag ${e.tag}: ${t}`)),t}if(e.commit){let t=e.commit.substring(0,8),r=Object.keys(o.versions).find(n=>n.includes(t));if(!r)throw new E(`Could not find npm version based on commit "${e.commit}"`);return console.log(B.green(`Version from commit ${e.commit}: ${r}`)),r}return e.mudVersion}function Yr(e,o){let{link:t}=o,{mudVersion:r}=o,n=Zr(e),s=Object.keys(te),d={};for(let i in n.dependencies)s.includes(i)&&(d[i]=n.dependencies[i]);let a={};for(let i in n.devDependencies)s.includes(i)&&(a[i]=n.devDependencies[i]);for(let i in n.dependencies)s.includes(i)&&(n.dependencies[i]=p(i,"dependencies"));for(let i in n.devDependencies)s.includes(i)&&(n.devDependencies[i]=p(i,"devDependencies"));return zr(e,JSON.stringify(n,null,2)+`
|
16
|
-
`),console.log(`Updating ${e}`),so(d,n.dependencies),so(a,n.devDependencies),n;function p(i,m){return t&&(r=Qr(e,t,i)),r||n[m][i]}}function Zr(e){try{let o=Kr(e,"utf8");return JSON.parse(o)}catch{throw new E("Could not read JSON at "+e)}}function so(e,o){for(let t in e)e[t]!==o[t]&&console.log(`${t}: ${B.red(e[t])} -> ${B.green(o[t])}`)}function Qr(e,o,t){let r=re.relative(re.dirname(e),process.cwd());return"link:"+re.join(r,o,te[t].localPath)}var ao=qr;import{anvil as Xr,forge as en,getRpcUrl as on}from"@latticexyz/common/foundry";import tn from"chalk";var rn={...M,port:{type:"number",description:"Port to run internal node for fork testing on",default:4242},worldAddress:{type:"string",description:"Address of an existing world contract. If provided, deployment is skipped and the RPC provided in the foundry.toml is used for fork testing."},forgeOptions:{type:"string",description:"Options to pass to forge test"}},nn={command:"test",describe:"Run tests in MUD contracts",builder(e){return e.options(rn)},async handler(e){if(!e.worldAddress){let n=["--block-base-fee-per-gas","0","--port",String(e.port)];Xr(n)}let o=e.worldAddress?await on(e.profile):`http://127.0.0.1:${e.port}`,t=e.worldAddress??(await $({...e,saveDeployment:!1,rpc:o})).address;console.log(tn.blue("World address",t));let r=e.forgeOptions?.replaceAll("\\","").split(" ")??[];try{await en(["test","--fork-url",o,...r],{profile:e.profile,env:{WORLD_ADDRESS:t}}),process.exit(0)}catch(n){console.error(n),process.exit(1)}}},io=nn;import{existsSync as sn,readFileSync as an}from"fs";import{ethers as co}from"ethers";import{loadConfig as cn}from"@latticexyz/config/node";import{MUDError as lo}from"@latticexyz/common/errors";import{cast as dn,getRpcUrl as ln,getSrcDirectory as mn}from"@latticexyz/common/foundry";import{resolveWorldConfig as pn}from"@latticexyz/world";import fn from"@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json"assert{type:"json"};import mo from"@latticexyz/world/mud.config.js";import{resourceToHex as fo}from"@latticexyz/common";import{createClient as un,http as yn}from"viem";import{getChainId as gn}from"viem/actions";var po=fo({type:"system",namespace:mo.namespace,name:mo.tables.Systems.name}),bn={command:"trace",describe:"Display the trace of a transaction",builder(e){return e.options({tx:{type:"string",required:!0,description:"Transaction hash to replay"},worldAddress:{type:"string",description:"World contract address. Defaults to the value from worlds.json, based on rpc's chainId"},configPath:{type:"string",description:"Path to the 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(e){e.profile??=process.env.FOUNDRY_PROFILE;let{profile:o}=e;e.srcDir??=await mn(o),e.rpc??=await ln(o);let{tx:t,configPath:r,srcDir:n,rpc:s}=e,d=P(n),a=await cn(r),p=pn(a,d.map(({basename:c})=>c)),i=e.worldAddress??await hn(a.worldsFile,s),m=new co.providers.StaticJsonRpcProvider(s),u=new co.Contract(i,fn,m),S=a.namespace,f=Object.values(p.systems).map(({name:c})=>c),g=await u.getFieldLayout(po),h=[];for(let c of f){let y=fo({type:"system",namespace:S,name:c}),w=await u.getField(po,[y],0,g);h.push({name:c,address:w})}let b=await dn(["run","--label",`${i}:World`,...h.map(({name:c,address:y})=>["--label",`${y}:${c}`]).flat(),`${t}`]);console.log(b),process.exit(0)}},uo=bn;async function hn(e,o){if(sn(e)){let t=un({transport:yn(o)}),r=await gn(t),n=JSON.parse(an(e,"utf-8"));if(!n[r])throw new lo(`chainId ${r} is missing in worldsFile "${e}"`);return n[r].address}else throw new lo("worldAddress is not specified and worldsFile is missing")}import{anvil as wn,getScriptDirectory as xn,getSrcDirectory as Cn}from"@latticexyz/common/foundry";import W from"chalk";import Sn from"chokidar";import{loadConfig as vn,resolveConfigPath as Dn}from"@latticexyz/config/node";import Tn from"path";import{homedir as kn}from"os";import{rmSync as An}from"fs";import{BehaviorSubject as In,debounceTime as On,exhaustMap as Pn,filter as Mn}from"rxjs";import{isDefined as Wn}from"@latticexyz/common/utils";var Fn={rpc:M.rpc,configPath:M.configPath,alwaysRunPostDeploy:M.alwaysRunPostDeploy},jn={command:"dev-contracts",describe:"Start a development server for MUD contracts",builder(e){return e.options(Fn)},async handler(e){let o=e.rpc,t=e.configPath??await Dn(e.configPath),r=await Cn(),n=await xn(),s=await vn(t);if(!e.rpc){console.log(W.gray("Cleaning devnode cache"));let i=kn();An(Tn.join(i,".foundry","anvil","tmp"),{recursive:!0,force:!0}),wn(["--block-time","1","--block-base-fee-per-gas","0"]),o="http://127.0.0.1:8545"}let d=new In(Date.now());Sn.watch([t,r,n],{ignoreInitial:!0}).on("all",async(i,m)=>{m.includes(t)&&(console.log(W.blue("Config changed, queuing deploy\u2026")),d.next(Date.now())),(m.includes(r)||m.includes(n))&&(m.includes(s.codegenDirectory)||(console.log(W.blue("Contracts changed, queuing deploy\u2026")),d.next(Date.now())))});let a;d.pipe(On(200),Pn(async i=>{a&&console.log(W.blue("Rebuilding and upgrading world\u2026"));try{let m=await $({...e,configPath:t,rpc:o,skipBuild:!1,printConfig:!1,profile:void 0,saveDeployment:!0,worldAddress:a,srcDir:r});return a=m.address,i<d.value?d.next(d.value):console.log(W.gray(`
|
17
|
-
Waiting for file changes\u2026
|
18
|
-
`)),m}catch(m){console.error(W.bgRed(W.whiteBright(`
|
19
|
-
Error while attempting deploy
|
20
|
-
`))),console.error(m),console.log(W.gray(`
|
21
|
-
Waiting for file changes\u2026
|
22
|
-
`))}}),Mn(Wn)).subscribe()}},yo=jn;var Dm=[eo,se,ie,Rn,ce,de,to,ao,io,uo,yo,$n];export{Dm as commands};
|
23
|
-
//# sourceMappingURL=commands-AAHOIIJW.js.map
|