@latticexyz/cli 2.0.0-snapshot-test-32d38619 → 2.0.0-transaction-context-af4b168c

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.
Files changed (75) hide show
  1. package/dist/chunk-22IIKR4S.js +4 -0
  2. package/dist/chunk-22IIKR4S.js.map +1 -0
  3. package/dist/commands-3JV3U43E.js +27 -0
  4. package/dist/commands-3JV3U43E.js.map +1 -0
  5. package/dist/errors-XGN6V2Y3.js +2 -0
  6. package/dist/errors-XGN6V2Y3.js.map +1 -0
  7. package/dist/index.js +0 -1
  8. package/dist/mud.js +1 -14
  9. package/dist/mud.js.map +1 -1
  10. package/package.json +18 -13
  11. package/src/build.ts +44 -0
  12. package/src/commands/build.ts +36 -0
  13. package/src/commands/deploy.ts +7 -30
  14. package/src/commands/dev-contracts.ts +76 -138
  15. package/src/commands/index.ts +2 -0
  16. package/src/commands/set-version.ts +23 -61
  17. package/src/commands/test.ts +30 -36
  18. package/src/commands/trace.ts +8 -6
  19. package/src/common.ts +1 -0
  20. package/src/debug.ts +10 -0
  21. package/src/deploy/common.ts +76 -0
  22. package/src/deploy/configToTables.ts +68 -0
  23. package/src/deploy/create2/README.md +9 -0
  24. package/src/deploy/create2/deployment.json +7 -0
  25. package/src/deploy/debug.ts +10 -0
  26. package/src/deploy/deploy.ts +116 -0
  27. package/src/deploy/deployWorld.ts +37 -0
  28. package/src/deploy/ensureContract.ts +61 -0
  29. package/src/deploy/ensureContractsDeployed.ts +25 -0
  30. package/src/deploy/ensureDeployer.ts +36 -0
  31. package/src/deploy/ensureFunctions.ts +86 -0
  32. package/src/deploy/ensureModules.ts +73 -0
  33. package/src/deploy/ensureNamespaceOwner.ts +71 -0
  34. package/src/deploy/ensureSystems.ts +162 -0
  35. package/src/deploy/ensureTables.ts +65 -0
  36. package/src/deploy/ensureWorldFactory.ts +118 -0
  37. package/src/deploy/getFunctions.ts +58 -0
  38. package/src/deploy/getResourceAccess.ts +51 -0
  39. package/src/deploy/getResourceIds.ts +31 -0
  40. package/src/deploy/getSystems.ts +48 -0
  41. package/src/deploy/getTableValue.ts +30 -0
  42. package/src/deploy/getTables.ts +59 -0
  43. package/src/deploy/getWorldDeploy.ts +39 -0
  44. package/src/deploy/logsToWorldDeploy.ts +49 -0
  45. package/src/deploy/resolveConfig.ts +151 -0
  46. package/src/deploy/resourceLabel.ts +3 -0
  47. package/src/index.ts +1 -1
  48. package/src/mud.ts +37 -31
  49. package/src/mudPackages.ts +24 -0
  50. package/src/runDeploy.ts +131 -0
  51. package/src/utils/modules/constants.ts +11 -8
  52. package/src/utils/utils/getContractData.ts +6 -3
  53. package/dist/chunk-WERDORTY.js +0 -11
  54. package/dist/chunk-WERDORTY.js.map +0 -1
  55. package/src/utils/deploy.ts +0 -255
  56. package/src/utils/deployHandler.ts +0 -93
  57. package/src/utils/modules/getInstallModuleCallData.ts +0 -27
  58. package/src/utils/modules/getUserModules.ts +0 -5
  59. package/src/utils/modules/types.ts +0 -14
  60. package/src/utils/systems/getGrantAccessCallData.ts +0 -29
  61. package/src/utils/systems/getRegisterFunctionSelectorsCallData.ts +0 -57
  62. package/src/utils/systems/getRegisterSystemCallData.ts +0 -17
  63. package/src/utils/systems/types.ts +0 -9
  64. package/src/utils/systems/utils.ts +0 -42
  65. package/src/utils/tables/getRegisterTableCallData.ts +0 -49
  66. package/src/utils/tables/getTableIds.ts +0 -21
  67. package/src/utils/tables/types.ts +0 -12
  68. package/src/utils/utils/confirmNonce.ts +0 -24
  69. package/src/utils/utils/deployContract.ts +0 -33
  70. package/src/utils/utils/fastTxExecute.ts +0 -56
  71. package/src/utils/utils/getChainId.ts +0 -10
  72. package/src/utils/utils/setInternalFeePerGas.ts +0 -49
  73. package/src/utils/utils/toBytes16.ts +0 -16
  74. package/src/utils/utils/types.ts +0 -21
  75. package/src/utils/world.ts +0 -28
@@ -0,0 +1,49 @@
1
+ import { AbiEventSignatureNotFoundError, Log, decodeEventLog, hexToString, parseAbi, trim } from "viem";
2
+ import { WorldDeploy, worldDeployEvents } from "./common";
3
+ import { isDefined } from "@latticexyz/common/utils";
4
+
5
+ export function logsToWorldDeploy(logs: readonly Log<bigint, number, false>[]): Omit<WorldDeploy, "stateBlock"> {
6
+ const deployLogs = logs
7
+ .map((log) => {
8
+ try {
9
+ return {
10
+ ...log,
11
+ ...decodeEventLog({
12
+ strict: true,
13
+ abi: parseAbi(worldDeployEvents),
14
+ topics: log.topics,
15
+ data: log.data,
16
+ }),
17
+ };
18
+ } catch (error: unknown) {
19
+ if (error instanceof AbiEventSignatureNotFoundError) {
20
+ return;
21
+ }
22
+ throw error;
23
+ }
24
+ })
25
+ .filter(isDefined);
26
+
27
+ // TODO: should this test for/validate that only one of each of these events is present? and that the address/block number don't change between each?
28
+ const { address, deployBlock, worldVersion, storeVersion } = deployLogs.reduce<Partial<WorldDeploy>>(
29
+ (deploy, log) => ({
30
+ ...deploy,
31
+ address: log.address,
32
+ deployBlock: log.blockNumber,
33
+ ...(log.eventName === "HelloWorld"
34
+ ? { worldVersion: hexToString(trim(log.args.worldVersion, { dir: "right" })) }
35
+ : null),
36
+ ...(log.eventName === "HelloStore"
37
+ ? { storeVersion: hexToString(trim(log.args.storeVersion, { dir: "right" })) }
38
+ : null),
39
+ }),
40
+ {}
41
+ );
42
+
43
+ if (address == null) throw new Error("could not find world address");
44
+ if (deployBlock == null) throw new Error("could not find world deploy block number");
45
+ if (worldVersion == null) throw new Error("could not find world version");
46
+ if (storeVersion == null) throw new Error("could not find store version");
47
+
48
+ return { address, deployBlock, worldVersion, storeVersion };
49
+ }
@@ -0,0 +1,151 @@
1
+ import { resolveWorldConfig } from "@latticexyz/world";
2
+ import { Config, ConfigInput, WorldFunction, salt } from "./common";
3
+ import { resourceToHex, hexToResource } from "@latticexyz/common";
4
+ import { resolveWithContext } from "@latticexyz/config";
5
+ import { encodeField } from "@latticexyz/protocol-parser";
6
+ import { SchemaAbiType, SchemaAbiTypeToPrimitiveType } from "@latticexyz/schema-type";
7
+ import {
8
+ getFunctionSelector,
9
+ Hex,
10
+ getCreate2Address,
11
+ getAddress,
12
+ hexToBytes,
13
+ Abi,
14
+ bytesToHex,
15
+ getFunctionSignature,
16
+ } from "viem";
17
+ import { getExistingContracts } from "../utils/getExistingContracts";
18
+ import { defaultModuleContracts } from "../utils/modules/constants";
19
+ import { getContractData } from "../utils/utils/getContractData";
20
+ import { configToTables } from "./configToTables";
21
+ import { deployer } from "./ensureDeployer";
22
+ import { resourceLabel } from "./resourceLabel";
23
+
24
+ // TODO: this should be replaced by https://github.com/latticexyz/mud/issues/1668
25
+
26
+ export function resolveConfig<config extends ConfigInput>({
27
+ config,
28
+ forgeSourceDir,
29
+ forgeOutDir,
30
+ }: {
31
+ config: config;
32
+ forgeSourceDir: string;
33
+ forgeOutDir: string;
34
+ }): Config<config> {
35
+ const tables = configToTables(config);
36
+
37
+ // TODO: should the config parser/loader help with resolving systems?
38
+ const contractNames = getExistingContracts(forgeSourceDir).map(({ basename }) => basename);
39
+ const resolvedConfig = resolveWorldConfig(config, contractNames);
40
+ const baseSystemContractData = getContractData("System", forgeOutDir);
41
+ const baseSystemFunctions = baseSystemContractData.abi
42
+ .filter((item): item is typeof item & { type: "function" } => item.type === "function")
43
+ .map(getFunctionSignature);
44
+
45
+ const systems = Object.entries(resolvedConfig.systems).map(([systemName, system]) => {
46
+ const namespace = config.namespace;
47
+ const name = system.name;
48
+ const systemId = resourceToHex({ type: "system", namespace, name });
49
+ const contractData = getContractData(systemName, forgeOutDir);
50
+
51
+ const systemFunctions = contractData.abi
52
+ .filter((item): item is typeof item & { type: "function" } => item.type === "function")
53
+ .map(getFunctionSignature)
54
+ .filter((sig) => !baseSystemFunctions.includes(sig))
55
+ .map((sig): WorldFunction => {
56
+ // TODO: figure out how to not duplicate contract behavior (https://github.com/latticexyz/mud/issues/1708)
57
+ const worldSignature = namespace === "" ? sig : `${namespace}__${sig}`;
58
+ return {
59
+ signature: worldSignature,
60
+ selector: getFunctionSelector(worldSignature),
61
+ systemId,
62
+ systemFunctionSignature: sig,
63
+ systemFunctionSelector: getFunctionSelector(sig),
64
+ };
65
+ });
66
+
67
+ return {
68
+ namespace,
69
+ name,
70
+ systemId,
71
+ allowAll: system.openAccess,
72
+ allowedAddresses: system.accessListAddresses as Hex[],
73
+ allowedSystemIds: system.accessListSystems.map((name) =>
74
+ resourceToHex({ type: "system", namespace, name: resolvedConfig.systems[name].name })
75
+ ),
76
+ address: getCreate2Address({ from: deployer, bytecode: contractData.bytecode, salt }),
77
+ bytecode: contractData.bytecode,
78
+ deployedBytecodeSize: contractData.deployedBytecodeSize,
79
+ abi: contractData.abi,
80
+ functions: systemFunctions,
81
+ };
82
+ });
83
+
84
+ // resolve allowedSystemIds
85
+ // TODO: resolve this at deploy time so we can allow for arbitrary system IDs registered in the world as the source-of-truth rather than config
86
+ const systemsWithAccess = systems.map(({ allowedAddresses, allowedSystemIds, ...system }) => {
87
+ const allowedSystemAddresses = allowedSystemIds.map((systemId) => {
88
+ const targetSystem = systems.find((s) => s.systemId === systemId);
89
+ if (!targetSystem) {
90
+ throw new Error(
91
+ `System ${resourceLabel(system)} wanted access to ${resourceLabel(
92
+ hexToResource(systemId)
93
+ )}, but it wasn't found in the config.`
94
+ );
95
+ }
96
+ return targetSystem.address;
97
+ });
98
+ return {
99
+ ...system,
100
+ allowedAddresses: Array.from(
101
+ new Set([...allowedAddresses, ...allowedSystemAddresses].map((addr) => getAddress(addr)))
102
+ ),
103
+ };
104
+ });
105
+
106
+ // ugh (https://github.com/latticexyz/mud/issues/1668)
107
+ const resolveContext = {
108
+ tableIds: Object.fromEntries(
109
+ Object.entries(config.tables).map(([tableName, table]) => [
110
+ tableName,
111
+ hexToBytes(
112
+ resourceToHex({
113
+ type: table.offchainOnly ? "offchainTable" : "table",
114
+ namespace: config.namespace,
115
+ name: table.name,
116
+ })
117
+ ),
118
+ ])
119
+ ),
120
+ };
121
+
122
+ const modules = config.modules.map((mod) => {
123
+ const contractData =
124
+ defaultModuleContracts.find((defaultMod) => defaultMod.name === mod.name) ??
125
+ getContractData(mod.name, forgeOutDir);
126
+ const installArgs = mod.args
127
+ .map((arg) => resolveWithContext(arg, resolveContext))
128
+ .map((arg) => {
129
+ const value = arg.value instanceof Uint8Array ? bytesToHex(arg.value) : arg.value;
130
+ return encodeField(arg.type as SchemaAbiType, value as SchemaAbiTypeToPrimitiveType<SchemaAbiType>);
131
+ });
132
+ if (installArgs.length > 1) {
133
+ throw new Error(`${mod.name} module should only have 0-1 args, but had ${installArgs.length} args.`);
134
+ }
135
+ return {
136
+ name: mod.name,
137
+ installAsRoot: mod.root,
138
+ installData: installArgs.length === 0 ? "0x" : installArgs[0],
139
+ address: getCreate2Address({ from: deployer, bytecode: contractData.bytecode, salt }),
140
+ bytecode: contractData.bytecode,
141
+ deployedBytecodeSize: contractData.deployedBytecodeSize,
142
+ abi: contractData.abi,
143
+ };
144
+ });
145
+
146
+ return {
147
+ tables,
148
+ systems: systemsWithAccess,
149
+ modules,
150
+ };
151
+ }
@@ -0,0 +1,3 @@
1
+ export function resourceLabel({ namespace, name }: { readonly namespace: string; readonly name: string }): string {
2
+ return `${namespace}:${name}`;
3
+ }
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export * from "./utils/deployHandler";
1
+ // nothing to export
package/src/mud.ts CHANGED
@@ -1,39 +1,45 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import yargs from "yargs";
4
- import { hideBin } from "yargs/helpers";
5
- import { commands } from "./commands";
6
- import { logError } from "./utils/errors";
7
-
8
3
  // Load .env file into process.env
9
4
  import * as dotenv from "dotenv";
10
- import chalk from "chalk";
11
5
  dotenv.config();
12
6
 
13
- yargs(hideBin(process.argv))
14
- // Explicit name to display in help (by default it's the entry file, which may not be "mud" for e.g. ts-node)
15
- .scriptName("mud")
16
- // Use the commands directory to scaffold
17
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- command array overload isn't typed, see https://github.com/yargs/yargs/blob/main/docs/advanced.md#esm-hierarchy
18
- .command(commands as any)
19
- // Enable strict mode.
20
- .strict()
21
- // Custom error handler
22
- .fail((msg, err) => {
23
- console.error(chalk.red(msg));
24
- if (msg.includes("Missing required argument")) {
25
- console.log(
26
- chalk.yellow(`Run 'pnpm mud ${process.argv[2]} --help' for a list of available and required arguments.`)
27
- );
28
- }
29
- console.log("");
30
- // Even though `.fail` type says we should get an `Error`, this can sometimes be undefined
31
- if (err != null) {
32
- logError(err);
7
+ async function run() {
8
+ // Import everything else async so they can pick up env vars in .env
9
+ const { default: yargs } = await import("yargs");
10
+ const { default: chalk } = await import("chalk");
11
+ const { hideBin } = await import("yargs/helpers");
12
+ const { logError } = await import("./utils/errors");
13
+ const { commands } = await import("./commands");
14
+
15
+ yargs(hideBin(process.argv))
16
+ // Explicit name to display in help (by default it's the entry file, which may not be "mud" for e.g. ts-node)
17
+ .scriptName("mud")
18
+ // Use the commands directory to scaffold
19
+ // command array overload isn't typed, see https://github.com/yargs/yargs/blob/main/docs/advanced.md#esm-hierarchy
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ .command(commands as any)
22
+ // Enable strict mode.
23
+ .strict()
24
+ // Custom error handler
25
+ .fail((msg, err) => {
26
+ console.error(chalk.red(msg));
27
+ if (msg.includes("Missing required argument")) {
28
+ console.log(
29
+ chalk.yellow(`Run 'pnpm mud ${process.argv[2]} --help' for a list of available and required arguments.`)
30
+ );
31
+ }
33
32
  console.log("");
34
- }
33
+ // Even though `.fail` type says we should get an `Error`, this can sometimes be undefined
34
+ if (err != null) {
35
+ logError(err);
36
+ console.log("");
37
+ }
38
+
39
+ process.exit(1);
40
+ })
41
+ // Useful aliases.
42
+ .alias({ h: "help" }).argv;
43
+ }
35
44
 
36
- process.exit(1);
37
- })
38
- // Useful aliases.
39
- .alias({ h: "help" }).argv;
45
+ run();
@@ -0,0 +1,24 @@
1
+ import { ZodError, z } from "zod";
2
+ import { MudPackages } from "./common";
3
+
4
+ const envSchema = z.object({
5
+ MUD_PACKAGES: z.string().transform((value) => JSON.parse(value) as MudPackages),
6
+ });
7
+
8
+ function parseEnv(): z.infer<typeof envSchema> {
9
+ try {
10
+ return envSchema.parse({
11
+ // tsup replaces the env vars with their values at compile time
12
+ MUD_PACKAGES: process.env.MUD_PACKAGES,
13
+ });
14
+ } catch (error) {
15
+ if (error instanceof ZodError) {
16
+ const { _errors, ...invalidEnvVars } = error.format();
17
+ console.error(`\nMissing or invalid environment variables:\n\n ${Object.keys(invalidEnvVars).join("\n ")}\n`);
18
+ process.exit(1);
19
+ }
20
+ throw error;
21
+ }
22
+ }
23
+
24
+ export const mudPackages = parseEnv().MUD_PACKAGES;
@@ -0,0 +1,131 @@
1
+ import path from "node:path";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { InferredOptionTypes, Options } from "yargs";
4
+ import { deploy } from "./deploy/deploy";
5
+ import { createWalletClient, http, Hex, isHex } from "viem";
6
+ import { privateKeyToAccount } from "viem/accounts";
7
+ import { loadConfig } from "@latticexyz/config/node";
8
+ import { StoreConfig } from "@latticexyz/store";
9
+ import { WorldConfig } from "@latticexyz/world";
10
+ import { getOutDirectory, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
11
+ import chalk from "chalk";
12
+ import { MUDError } from "@latticexyz/common/errors";
13
+ import { resolveConfig } from "./deploy/resolveConfig";
14
+ import { getChainId } from "viem/actions";
15
+ import { postDeploy } from "./utils/utils/postDeploy";
16
+ import { WorldDeploy } from "./deploy/common";
17
+ import { build } from "./build";
18
+
19
+ export const deployOptions = {
20
+ configPath: { type: "string", desc: "Path to the config file" },
21
+ printConfig: { type: "boolean", desc: "Print the resolved config" },
22
+ profile: { type: "string", desc: "The foundry profile to use" },
23
+ saveDeployment: { type: "boolean", desc: "Save the deployment info to a file", default: true },
24
+ rpc: { type: "string", desc: "The RPC URL to use. Defaults to the RPC url from the local foundry.toml" },
25
+ worldAddress: { type: "string", desc: "Deploy to an existing World at the given address" },
26
+ srcDir: { type: "string", desc: "Source directory. Defaults to foundry src directory." },
27
+ skipBuild: { type: "boolean", desc: "Skip rebuilding the contracts before deploying" },
28
+ alwaysRunPostDeploy: {
29
+ type: "boolean",
30
+ 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.",
31
+ },
32
+ salt: {
33
+ type: "string",
34
+ desc: "The deployment salt to use. Defaults to a random salt.",
35
+ },
36
+ } as const satisfies Record<string, Options>;
37
+
38
+ export type DeployOptions = InferredOptionTypes<typeof deployOptions>;
39
+
40
+ /**
41
+ * Given some CLI arguments, finds and resolves a MUD config, foundry profile, and runs a deploy.
42
+ * This is used by the deploy, test, and dev-contracts CLI commands.
43
+ */
44
+ export async function runDeploy(opts: DeployOptions): Promise<WorldDeploy> {
45
+ const salt = opts.salt;
46
+ if (salt != null && !isHex(salt)) {
47
+ throw new MUDError("Expected hex string for salt");
48
+ }
49
+
50
+ const profile = opts.profile ?? process.env.FOUNDRY_PROFILE;
51
+
52
+ const config = (await loadConfig(opts.configPath)) as StoreConfig & WorldConfig;
53
+ if (opts.printConfig) {
54
+ console.log(chalk.green("\nResolved config:\n"), JSON.stringify(config, null, 2));
55
+ }
56
+
57
+ const srcDir = opts.srcDir ?? (await getSrcDirectory(profile));
58
+ const outDir = await getOutDirectory(profile);
59
+
60
+ const rpc = opts.rpc ?? (await getRpcUrl(profile));
61
+ console.log(
62
+ chalk.bgBlue(
63
+ chalk.whiteBright(`\n Deploying MUD contracts${profile ? " with profile " + profile : ""} to RPC ${rpc} \n`)
64
+ )
65
+ );
66
+
67
+ // Run build
68
+ if (!opts.skipBuild) {
69
+ await build({ config, srcDir, foundryProfile: profile });
70
+ }
71
+
72
+ const privateKey = process.env.PRIVATE_KEY as Hex;
73
+ if (!privateKey) {
74
+ throw new MUDError(
75
+ `Missing PRIVATE_KEY environment variable.
76
+ Run 'echo "PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" > .env'
77
+ in your contracts directory to use the default anvil private key.`
78
+ );
79
+ }
80
+
81
+ const resolvedConfig = resolveConfig({ config, forgeSourceDir: srcDir, forgeOutDir: outDir });
82
+
83
+ const client = createWalletClient({
84
+ transport: http(rpc),
85
+ account: privateKeyToAccount(privateKey),
86
+ });
87
+ console.log("Deploying from", client.account.address);
88
+
89
+ const startTime = Date.now();
90
+ const worldDeploy = await deploy({
91
+ salt,
92
+ worldAddress: opts.worldAddress as Hex | undefined,
93
+ client,
94
+ config: resolvedConfig,
95
+ });
96
+ if (opts.worldAddress == null || opts.alwaysRunPostDeploy) {
97
+ await postDeploy(config.postDeployScript, worldDeploy.address, rpc, profile);
98
+ }
99
+ console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds"));
100
+
101
+ const deploymentInfo = {
102
+ worldAddress: worldDeploy.address,
103
+ blockNumber: Number(worldDeploy.deployBlock),
104
+ };
105
+
106
+ if (opts.saveDeployment) {
107
+ const chainId = await getChainId(client);
108
+ const deploysDir = path.join(config.deploysDirectory, chainId.toString());
109
+ mkdirSync(deploysDir, { recursive: true });
110
+ writeFileSync(path.join(deploysDir, "latest.json"), JSON.stringify(deploymentInfo, null, 2));
111
+ writeFileSync(path.join(deploysDir, Date.now() + ".json"), JSON.stringify(deploymentInfo, null, 2));
112
+
113
+ const localChains = [1337, 31337];
114
+ const deploys = existsSync(config.worldsFile) ? JSON.parse(readFileSync(config.worldsFile, "utf-8")) : {};
115
+ deploys[chainId] = {
116
+ address: deploymentInfo.worldAddress,
117
+ // We expect the worlds file to be committed and since local deployments are often
118
+ // a consistent address but different block number, we'll ignore the block number.
119
+ blockNumber: localChains.includes(chainId) ? undefined : deploymentInfo.blockNumber,
120
+ };
121
+ writeFileSync(config.worldsFile, JSON.stringify(deploys, null, 2));
122
+
123
+ console.log(
124
+ chalk.bgGreen(chalk.whiteBright(`\n Deployment result (written to ${config.worldsFile} and ${deploysDir}): \n`))
125
+ );
126
+ }
127
+
128
+ console.log(deploymentInfo);
129
+
130
+ return worldDeploy;
131
+ }
@@ -1,23 +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 { ContractCode } from "../utils/types";
4
+ import { Abi, Hex, size } from "viem";
5
5
 
6
6
  // These modules are always deployed
7
- export const defaultModuleContracts: ContractCode[] = [
7
+ export const defaultModuleContracts = [
8
8
  {
9
9
  name: "KeysWithValueModule",
10
- abi: KeysWithValueModuleData.abi,
11
- bytecode: KeysWithValueModuleData.bytecode,
10
+ abi: KeysWithValueModuleData.abi as Abi,
11
+ bytecode: KeysWithValueModuleData.bytecode.object as Hex,
12
+ deployedBytecodeSize: size(KeysWithValueModuleData.deployedBytecode.object as Hex),
12
13
  },
13
14
  {
14
15
  name: "KeysInTableModule",
15
- abi: KeysInTableModuleData.abi,
16
- bytecode: KeysInTableModuleData.bytecode,
16
+ abi: KeysInTableModuleData.abi as Abi,
17
+ bytecode: KeysInTableModuleData.bytecode.object as Hex,
18
+ deployedBytecodeSize: size(KeysInTableModuleData.deployedBytecode.object as Hex),
17
19
  },
18
20
  {
19
21
  name: "UniqueEntityModule",
20
- abi: UniqueEntityModuleData.abi,
21
- bytecode: UniqueEntityModuleData.bytecode,
22
+ abi: UniqueEntityModuleData.abi as Abi,
23
+ bytecode: UniqueEntityModuleData.bytecode.object as Hex,
24
+ deployedBytecodeSize: size(UniqueEntityModuleData.deployedBytecode.object as Hex),
22
25
  },
23
26
  ];
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "fs";
2
2
  import path from "path";
3
- import { Fragment } from "ethers/lib/utils.js";
4
3
  import { MUDError } from "@latticexyz/common/errors";
4
+ import { Abi, Hex, size } from "viem";
5
5
 
6
6
  /**
7
7
  * Load the contract's abi and bytecode from the file system
@@ -10,7 +10,7 @@ import { MUDError } from "@latticexyz/common/errors";
10
10
  export function getContractData(
11
11
  contractName: string,
12
12
  forgeOutDirectory: string
13
- ): { bytecode: string; abi: Fragment[] } {
13
+ ): { bytecode: Hex; abi: Abi; deployedBytecodeSize: number } {
14
14
  let data: any;
15
15
  const contractDataPath = path.join(forgeOutDirectory, contractName + ".sol", contractName + ".json");
16
16
  try {
@@ -22,8 +22,11 @@ export function getContractData(
22
22
  const bytecode = data?.bytecode?.object;
23
23
  if (!bytecode) throw new MUDError(`No bytecode found in ${contractDataPath}`);
24
24
 
25
+ const deployedBytecode = data?.deployedBytecode?.object;
26
+ if (!deployedBytecode) throw new MUDError(`No deployed bytecode found in ${contractDataPath}`);
27
+
25
28
  const abi = data?.abi;
26
29
  if (!abi) throw new MUDError(`No ABI found in ${contractDataPath}`);
27
30
 
28
- return { abi, bytecode };
31
+ return { abi, bytecode, deployedBytecodeSize: size(deployedBytecode as Hex) };
29
32
  }
@@ -1,11 +0,0 @@
1
- import S from"chalk";import B from"path";import{MUDError as it}from"@latticexyz/common/errors";import{loadConfig as ct}from"@latticexyz/config/node";import d from"chalk";import ze from"path";import{ethers as $}from"ethers";import{getOutDirectory as Qe,cast as Xe,getSrcDirectory as Ze,getRemappings as et}from"@latticexyz/common/foundry";import{resolveWorldConfig as tt}from"@latticexyz/world";import Ie from"chalk";import Me from"@latticexyz/world/out/World.sol/World.json"assert{type:"json"};import ve from"@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json"assert{type:"json"};import U from"chalk";import{ethers as Se}from"ethers";import{MUDError as K}from"@latticexyz/common/errors";async function I(e){let{signer:o,nonce:t,maxPriorityFeePerGas:r,maxFeePerGas:n,debug:a,gasPrice:l,confirmations:p,contract:m}=e;try{let i=new Se.ContractFactory(m.abi,m.bytecode,o);console.log(U.gray(`executing deployment of ${m.name} with nonce ${t}`));let g=i.deploy({nonce:t,maxPriorityFeePerGas:r,maxFeePerGas:n,gasPrice:l}).then(s=>p?s:s.deployed()),{address:f}=await g;return console.log(U.green("Deployed",m.name,"to",f)),f}catch(i){throw a&&console.error(i),i?.message.includes("invalid bytecode")?new K(`Error deploying ${m.name}: invalid bytecode. Note that linking of public libraries is not supported yet, make sure none of your libraries use "external" functions.`):i?.message.includes("CreateContractLimit")?new K(`Error deploying ${m.name}: CreateContractLimit exceeded.`):i}}import{readFileSync as Te}from"fs";import Fe from"path";import{MUDError as R}from"@latticexyz/common/errors";function w(e,o){let t,r=Fe.join(o,e+".sol",e+".json");try{t=JSON.parse(Te(r,"utf8"))}catch{throw new R(`Error reading file at ${r}`)}let n=t?.bytecode?.object;if(!n)throw new R(`No bytecode found in ${r}`);let a=t?.abi;if(!a)throw new R(`No ABI found in ${r}`);return{abi:a,bytecode:n}}async function L(e){console.log(Ie.blue("Deploying World"));let o=e.worldContractName?{name:"World",...w(e.worldContractName,e.forgeOutDirectory)}:{abi:ve,bytecode:Me.bytecode,name:"World"};return I({...e,nonce:e.nonce,contract:o})}import ot from"@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json"assert{type:"json"};import le from"@latticexyz/world/out/CoreModule.sol/CoreModule.json"assert{type:"json"};import H from"@latticexyz/world-modules/out/KeysWithValueModule.sol/KeysWithValueModule.json"assert{type:"json"};import J from"@latticexyz/world-modules/out/KeysInTableModule.sol/KeysInTableModule.json"assert{type:"json"};import _ from"@latticexyz/world-modules/out/UniqueEntityModule.sol/UniqueEntityModule.json"assert{type:"json"};var N=[{name:"KeysWithValueModule",abi:H.abi,bytecode:H.bytecode},{name:"KeysInTableModule",abi:J.abi,bytecode:J.bytecode},{name:"UniqueEntityModule",abi:_.abi,bytecode:_.bytecode}];import{defaultAbiCoder as Re}from"ethers/lib/utils.js";import{resolveWithContext as Ne}from"@latticexyz/config";async function q(e,o,t){let r=await e[o.name];if(!r)throw new Error(`Module ${o.name} not found`);let n=o.args.map(p=>Ne(p,{tableIds:t})),a=n.map(p=>p.value),l=n.map(p=>p.type);return{func:o.root?"installRootModule":"installModule",args:[r,Re.encode(l,a)]}}function V(e,o){return o.filter(t=>!e.some(r=>r.name===t.name))}import{resourceIdToHex as Ae}from"@latticexyz/common";async function z(e){let{systems:o,namespace:t,systemContracts:r}=e,n=[];for(let{name:a,accessListAddresses:l,accessListSystems:p}of o)l.map(async m=>n.push(Y(a,t,m))),p.map(async m=>n.push(Y(a,t,await r[m])));return n}function Y(e,o,t){return{func:"grantAccess",args:[Ae({type:"system",namespace:o,name:e}),t]}}import{resourceIdToHex as Z}from"@latticexyz/common";import{ethers as A}from"ethers";function k(e,o){let{abi:t}=w(e,o);return t.filter(r=>["fallback","function"].includes(r.type)).map(r=>`${r.name}${X(r.inputs)}`)}function Q(e){return ke(e)}function X(e){return`(${e.map(t=>{let r=t.type.match(/tuple(.*)/);return r?X(t.components)+r[1]:t.type})})`}function ke(e){return A.utils.hexDataSlice(A.utils.keccak256(A.utils.toUtf8Bytes(e)),0,4)}function ee(e){let o=[],{systemContractName:t,namespace:r,forgeOutDirectory:n,system:a}=e;if(a.registerFunctionSelectors){let l=k("System",n),p=k(t,n).filter(i=>t==="System"||!l.includes(i)),m=r==="";for(let i of p)o.push(je({namespace:r,name:a.name,systemFunctionSignature:i,isRoot:m}))}return o}function je(e){let{namespace:o,name:t,systemFunctionSignature:r,isRoot:n}=e;if(n){let a=Q(r);return{func:"registerRootFunctionSelector",args:[Z({type:"system",namespace:o,name:t}),r,a]}}else return{func:"registerFunctionSelector",args:[Z({type:"system",namespace:o,name:t}),r]}}import{resourceIdToHex as Oe}from"@latticexyz/common";async function te(e){let{namespace:o,systemContracts:t,systemKey:r,system:n}=e,a=await t[r];return{func:"registerSystem",args:[Oe({type:"system",namespace:o,name:n.name}),a,n.openAccess]}}import{encodeSchema as oe,getStaticByteLength as $e}from"@latticexyz/schema-type/deprecated";import{resolveAbiOrUserType as re}from"@latticexyz/store/codegen";import{resourceIdToHex as Be}from"@latticexyz/common";import{fieldLayoutToHex as Ee}from"@latticexyz/protocol-parser";import{loadAndExtractUserTypes as We}from"@latticexyz/common/codegen";function ne(e,o,t,r){let{name:n,valueSchema:a,keySchema:l}=e;if(!n)throw Error("Table missing name");let p=We(o.userTypes,t,r),m=Object.values(a).map(s=>{let{schemaType:u}=re(s,o,p);return u}),i=m.map(s=>$e(s)),g={staticFieldLengths:i.filter(s=>s>0),numDynamicFields:i.filter(s=>s===0).length},f=Object.values(l).map(s=>{let{schemaType:u}=re(s,o,p);return u});return{func:"registerTable",args:[Be({type:e.offchainOnly?"offchainTable":"table",namespace:o.namespace,name:n}),Ee(g),oe(f),oe(m),Object.keys(l),Object.keys(a)]}}function j(e){if(e.length>16)throw new Error("String does not fit into 16 bytes");let o=new Uint8Array(16);for(let t=0;t<e.length;t++)o[t]=e.charCodeAt(t);for(let t=e.length;t<16;t++)o[t]=0;return o}function ae(e){let o={};for(let[t,{name:r}]of Object.entries(e.tables))o[t]=Ge(e.namespace,r);return o}function Ge(e,o){let t=j(e),r=j(o),n=new Uint8Array(32);return n.set(t),n.set(r,16),n}import Ue from"chalk";import{MUDError as Ke}from"@latticexyz/common/errors";async function se(e,o,t){let r=await e.getTransactionCount(),n=0,a=100;for(;r!==o&&n<a;)console.log(Ue.gray(`Waiting for transactions to be included before executing postDeployScript (local nonce: ${o}, remote nonce: ${r}, retry number ${n}/${a})`)),await new Promise(l=>setTimeout(l,t)),n++,r=await e.getTransactionCount();if(r!==o)throw new Ke("Remote nonce doesn't match local nonce, indicating that not all deploy transactions were included.")}import Le from"chalk";import{MUDError as He}from"@latticexyz/common/errors";async function h(e){let{func:o,args:t,contract:r,signer:n,nonce:a,maxPriorityFeePerGas:l,maxFeePerGas:p,gasPrice:m,confirmations:i=1,debug:g}=e,f=`${o}(${t.map(s=>`'${s}'`).join(",")})`;try{let s=r.connect(n),u=await s.estimateGas[o].apply(null,t);return console.log(Le.gray(`executing transaction: ${f} with nonce ${a}`)),s[o].apply(null,[...t,{gasLimit:u,nonce:a,maxPriorityFeePerGas:l,maxFeePerGas:p,gasPrice:m}]).then(D=>i===0?D:D.wait(i))}catch(s){throw g&&console.error(s),new He(`Gas estimation error for ${f}: ${s?.reason}`)}}import{existsSync as Je}from"fs";import _e from"path";import qe from"chalk";import{getScriptDirectory as Ve,forge as Ye}from"@latticexyz/common/foundry";async function ie(e,o,t,r){let n=_e.join(await Ve(),e+".s.sol");Je(n)?(console.log(qe.blue(`Executing post deploy script at ${n}`)),await Ye(["script",e,"--sig","run(address)",o,"--broadcast","--rpc-url",t,"-vvv"],{profile:r})):console.log(`No script at ${n}, skipping post deploy hook`)}import{MUDError as O}from"@latticexyz/common/errors";async function ce(e,o){let t=await e.provider.getFeeData(),r,n,a;if(t.lastBaseFeePerGas){if(!t.lastBaseFeePerGas.eq(0)&&(await e.getBalance()).eq(0))throw new O(`Attempting to deploy to a chain with non-zero base fee with an account that has no balance.
2
- If you're deploying to the Lattice testnet, you can fund your account by running 'pnpm mud faucet --address ${await e.getAddress()}'`);r=t.lastBaseFeePerGas.eq(0)?0:Math.floor(15e8*o),n=t.lastBaseFeePerGas.mul(2).add(r)}else if(t.gasPrice){if(!t.gasPrice.eq(0)&&(await e.getBalance()).eq(0))throw new O("Attempting to deploy to a chain with non-zero gas price with an account that has no balance.");a=t.gasPrice}else throw new O("Can not fetch fee data from RPC");return{maxPriorityFeePerGas:r,maxFeePerGas:n,gasPrice:a}}import{resourceIdToHex as rt}from"@latticexyz/common";async function me(e,o,t){let r=Date.now(),{profile:n,rpc:a,privateKey:l,priorityFeeMultiplier:p,debug:m,worldAddress:i,disableTxWait:g,pollInterval:f}=t,s=tt(e,o),u=await Qe(n),D=await et(n),T=ze.join(await Ze(n),e.codegenDirectory),W=new $.providers.StaticJsonRpcProvider(a);W.pollingInterval=f;let P=new $.Wallet(l,W);console.log("Deploying from",P.address);let y=await P.getTransactionCount();console.log("Initial nonce",y);let C={...await ce(P,p),signer:P,debug:!!m,disableTxWait:g,confirmations:g?0:1},G=Number(await Xe(["block-number","--rpc-url",a],{profile:n}));console.log("Start deployment at block",G);let de=i?Promise.resolve(i):L({...C,nonce:y++,worldContractName:e.worldContractName,forgeOutDirectory:u}),ge=V(N,e.modules),ye=Object.keys(ge).map(c=>{let{abi:b,bytecode:v}=w(c,u);return{name:c,abi:b,bytecode:v}}),be=Object.keys(s.systems).map(c=>{let{abi:b,bytecode:v}=w(c,u);return{name:c,abi:b,bytecode:v}}),F=[{name:"CoreModule",abi:le.abi,bytecode:le.bytecode},...N,...ye,...be].reduce((c,b)=>(c[b.name]=I({...C,nonce:y++,contract:b}),c),{}),M=await de,x=new $.Contract(M,ot);i||(console.log(d.blue("Installing CoreModule")),await h({...C,nonce:y++,contract:x,func:"initialize",args:[await F.CoreModule]}),console.log(d.green("Installed CoreModule"))),e.namespace&&(console.log(d.blue("Registering Namespace")),await h({...C,nonce:y++,contract:x,func:"registerNamespace",args:[rt({type:"namespace",namespace:e.namespace,name:""})]}),console.log(d.green("Namespace registered")));let Ce=ae(e),we=Object.values(e.tables).map(c=>ne(c,e,T,D));console.log(d.blue("Registering tables")),await Promise.all(we.map(c=>h({...C,nonce:y++,contract:x,...c}))),console.log(d.green("Tables registered")),console.log(d.blue("Registering Systems and Functions"));let he=await Promise.all(Object.entries(s.systems).map(([c,b])=>te({systemContracts:F,systemKey:c,system:b,namespace:e.namespace}))),De=Object.entries(s.systems).flatMap(([c,b])=>ee({systemContractName:c,system:b,namespace:e.namespace,forgeOutDirectory:u}));await Promise.all([...he,...De].map(c=>h({...C,nonce:y++,contract:x,...c}))),console.log(d.green("Systems and Functions registered"));let xe=await z({systems:Object.values(s.systems),systemContracts:F,namespace:e.namespace});console.log(d.blue("Granting Access")),await Promise.all(xe.map(c=>h({...C,nonce:y++,contract:x,...c}))),console.log(d.green("Access granted"));let Pe=await Promise.all(e.modules.map(c=>q(F,c,Ce)));return console.log(d.blue("Installing User Modules")),await Promise.all(Pe.map(c=>h({...C,nonce:y++,contract:x,...c}))),console.log(d.green("User Modules Installed")),await se(P,y,f),await ie(e.postDeployScript,M,a,n),console.log(d.green("Deployment completed in",(Date.now()-r)/1e3,"seconds")),{worldAddress:M,blockNumber:G}}import{forge as ue,getRpcUrl as lt,getSrcDirectory as mt}from"@latticexyz/common/foundry";import{existsSync as pt,mkdirSync as ft,readFileSync as ut,writeFileSync as E}from"fs";import nt from"glob";import{basename as at}from"path";function pe(e){return nt.sync(`${e}/**/*.sol`).map(o=>({path:o,basename:at(o,".sol")}))}import{execa as dt}from"execa";import{ethers as st}from"ethers";async function fe(e){let{result:o}=await st.utils.fetchJson(e,'{ "id": 42, "jsonrpc": "2.0", "method": "eth_chainId", "params": [ ] }');return Number(o)}async function nr(e){e.profile??=process.env.FOUNDRY_PROFILE;let{configPath:o,printConfig:t,profile:r,clean:n,skipBuild:a}=e,l=e.rpc??await lt(r);console.log(S.bgBlue(S.whiteBright(`
3
- Deploying MUD contracts${r?" with profile "+r:""} to RPC ${l}
4
- `))),n&&await ue(["clean"],{profile:r}),a||(await ue(["build","--skip","test","script"],{profile:r}),await dt("mud",["abi-ts"],{stdio:"inherit"}));let p=e?.srcDir??await mt(),m=pe(p).map(({basename:s})=>s),i=await ct(o);t&&console.log(S.green(`
5
- Resolved config:
6
- `),JSON.stringify(i,null,2));let g=process.env.PRIVATE_KEY;if(!g)throw new it(`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 f=await me(i,m,{...e,rpc:l,privateKey:g});if(e.saveDeployment){let s=await fe(l),u=B.join(i.deploysDirectory,s.toString());ft(u,{recursive:!0}),E(B.join(u,"latest.json"),JSON.stringify(f,null,2)),E(B.join(u,Date.now()+".json"),JSON.stringify(f,null,2));let D=[1337,31337],T=pt(i.worldsFile)?JSON.parse(ut(i.worldsFile,"utf-8")):{};T[s]={address:f.worldAddress,blockNumber:D.includes(s)?void 0:f.blockNumber},E(i.worldsFile,JSON.stringify(T,null,2)),console.log(S.bgGreen(S.whiteBright(`
9
- Deployment result (written to ${i.worldsFile} and ${u}):
10
- `)))}return console.log(f),f}export{pe as a,fe as b,nr as c};
11
- //# sourceMappingURL=chunk-WERDORTY.js.map