@latticexyz/cli 2.0.0-next.12 → 2.0.0-next.14
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/chunk-22IIKR4S.js +4 -0
- package/dist/chunk-22IIKR4S.js.map +1 -0
- package/dist/commands-UYAATBY6.js +27 -0
- package/dist/commands-UYAATBY6.js.map +1 -0
- package/dist/errors-XGN6V2Y3.js +2 -0
- package/dist/errors-XGN6V2Y3.js.map +1 -0
- package/dist/mud.js +1 -24
- package/dist/mud.js.map +1 -1
- package/package.json +12 -12
- package/src/commands/dev-contracts.ts +2 -1
- package/src/commands/trace.ts +1 -1
- package/src/deploy/common.ts +6 -2
- 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/mud.ts +37 -31
- package/src/utils/modules/constants.ts +10 -6
- package/src/utils/utils/getContractData.ts +9 -3
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/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/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/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
|
-
|
14
|
-
//
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
//
|
31
|
-
|
32
|
-
|
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
|
-
|
37
|
-
})
|
38
|
-
// Useful aliases.
|
39
|
-
.alias({ h: "help" }).argv;
|
45
|
+
run();
|
@@ -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
|
}
|