@latticexyz/cli 2.0.0-main-e1dc88eb → 2.0.0-main-ccc21e91

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latticexyz/cli",
3
- "version": "2.0.0-main-e1dc88eb",
3
+ "version": "2.0.0-main-ccc21e91",
4
4
  "description": "Command line interface for mud",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,17 +40,17 @@
40
40
  "yargs": "^17.7.1",
41
41
  "zod": "^3.21.4",
42
42
  "zod-validation-error": "^1.3.0",
43
- "@latticexyz/abi-ts": "2.0.0-main-e1dc88eb",
44
- "@latticexyz/common": "2.0.0-main-e1dc88eb",
45
- "@latticexyz/config": "2.0.0-main-e1dc88eb",
46
- "@latticexyz/gas-report": "2.0.0-main-e1dc88eb",
47
- "@latticexyz/protocol-parser": "2.0.0-main-e1dc88eb",
48
- "@latticexyz/schema-type": "2.0.0-main-e1dc88eb",
49
- "@latticexyz/services": "2.0.0-main-e1dc88eb",
50
- "@latticexyz/store": "2.0.0-main-e1dc88eb",
51
- "@latticexyz/utils": "2.0.0-main-e1dc88eb",
52
- "@latticexyz/world": "2.0.0-main-e1dc88eb",
53
- "@latticexyz/world-modules": "2.0.0-main-e1dc88eb"
43
+ "@latticexyz/abi-ts": "2.0.0-main-ccc21e91",
44
+ "@latticexyz/common": "2.0.0-main-ccc21e91",
45
+ "@latticexyz/config": "2.0.0-main-ccc21e91",
46
+ "@latticexyz/gas-report": "2.0.0-main-ccc21e91",
47
+ "@latticexyz/protocol-parser": "2.0.0-main-ccc21e91",
48
+ "@latticexyz/schema-type": "2.0.0-main-ccc21e91",
49
+ "@latticexyz/services": "2.0.0-main-ccc21e91",
50
+ "@latticexyz/store": "2.0.0-main-ccc21e91",
51
+ "@latticexyz/utils": "2.0.0-main-ccc21e91",
52
+ "@latticexyz/world": "2.0.0-main-ccc21e91",
53
+ "@latticexyz/world-modules": "2.0.0-main-ccc21e91"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/debug": "^4.1.7",
@@ -15,6 +15,7 @@ import { Address } from "viem";
15
15
  const devOptions = {
16
16
  rpc: deployOptions.rpc,
17
17
  configPath: deployOptions.configPath,
18
+ alwaysRunPostDeploy: deployOptions.alwaysRunPostDeploy,
18
19
  };
19
20
 
20
21
  const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof devOptions>> = {
@@ -74,6 +75,7 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
74
75
  }
75
76
  // TODO: handle errors
76
77
  const deploy = await runDeploy({
78
+ ...opts,
77
79
  configPath,
78
80
  rpc,
79
81
  skipBuild: false,
@@ -38,25 +38,25 @@ export type WorldFunction = {
38
38
  readonly systemFunctionSelector: Hex;
39
39
  };
40
40
 
41
- export type System = {
41
+ export type DeterministicContract = {
42
+ readonly address: Address;
43
+ readonly bytecode: Hex;
44
+ readonly abi: Abi;
45
+ };
46
+
47
+ export type System = DeterministicContract & {
42
48
  readonly namespace: string;
43
49
  readonly name: string;
44
50
  readonly systemId: Hex;
45
51
  readonly allowAll: boolean;
46
52
  readonly allowedAddresses: readonly Hex[];
47
- readonly address: Address;
48
- readonly bytecode: Hex;
49
- readonly abi: Abi;
50
53
  readonly functions: readonly WorldFunction[];
51
54
  };
52
55
 
53
- export type Module = {
56
+ export type Module = DeterministicContract & {
54
57
  readonly name: string;
55
58
  readonly installAsRoot: boolean;
56
59
  readonly installData: Hex; // TODO: figure out better naming for this
57
- readonly address: Address;
58
- readonly bytecode: Hex;
59
- readonly abi: Abi;
60
60
  };
61
61
 
62
62
  export type ConfigInput = StoreConfig & WorldConfig;
@@ -11,6 +11,9 @@ import { ensureModules } from "./ensureModules";
11
11
  import { Table } from "./configToTables";
12
12
  import { assertNamespaceOwner } from "./assertNamespaceOwner";
13
13
  import { debug } from "./debug";
14
+ import { resourceLabel } from "./resourceLabel";
15
+ import { ensureContract } from "./ensureContract";
16
+ import { uniqueBy } from "@latticexyz/common/utils";
14
17
 
15
18
  type DeployOptions<configInput extends ConfigInput> = {
16
19
  client: Client<Transport, Chain | undefined, Account>;
@@ -51,6 +54,27 @@ export async function deploy<configInput extends ConfigInput>({
51
54
  resourceIds: [...tables.map((table) => table.tableId), ...systems.map((system) => system.systemId)],
52
55
  });
53
56
 
57
+ // deploy all dependent contracts, because system registration, module install, etc. all expect these contracts to be callable.
58
+ const contractTxs = (
59
+ await Promise.all([
60
+ ...uniqueBy(systems, (system) => system.address).map((system) =>
61
+ ensureContract({ client, bytecode: system.bytecode, label: `${resourceLabel(system)} system` })
62
+ ),
63
+ ...uniqueBy(config.modules, (mod) => mod.address).map((mod) =>
64
+ ensureContract({ client, bytecode: mod.bytecode, label: `${mod.name} module` })
65
+ ),
66
+ ])
67
+ ).flat();
68
+
69
+ if (contractTxs.length) {
70
+ debug("waiting for contracts");
71
+ // wait for each tx separately/serially, because parallelizing results in RPC errors
72
+ for (const tx of contractTxs) {
73
+ await waitForTransactionReceipt(client, { hash: tx });
74
+ // TODO: throw if there was a revert?
75
+ }
76
+ }
77
+
54
78
  const tableTxs = await ensureTables({
55
79
  client,
56
80
  worldDeploy,
@@ -75,7 +99,7 @@ export async function deploy<configInput extends ConfigInput>({
75
99
  const txs = [...tableTxs, ...systemTxs, ...functionTxs, ...moduleTxs];
76
100
 
77
101
  // wait for each tx separately/serially, because parallelizing results in RPC errors
78
- debug("waiting for transactions to confirm");
102
+ debug("waiting for all transactions to confirm");
79
103
  for (const tx of txs) {
80
104
  await waitForTransactionReceipt(client, { hash: tx });
81
105
  // TODO: throw if there was a revert?
@@ -1,9 +1,8 @@
1
1
  import { Client, Transport, Chain, Account, Hex } from "viem";
2
2
  import { writeContract } from "@latticexyz/common";
3
3
  import { Module, WorldDeploy, worldAbi } from "./common";
4
- import { ensureContract } from "./ensureContract";
5
4
  import { debug } from "./debug";
6
- import { uniqueBy, wait } from "@latticexyz/common/utils";
5
+ import { wait } from "@latticexyz/common/utils";
7
6
  import pRetry from "p-retry";
8
7
 
9
8
  export async function ensureModules({
@@ -17,14 +16,6 @@ export async function ensureModules({
17
16
  }): Promise<readonly Hex[]> {
18
17
  if (!modules.length) return [];
19
18
 
20
- // kick off contract deployments first, otherwise installing modules can fail
21
- const contractTxs = await Promise.all(
22
- uniqueBy(modules, (mod) => mod.address).map((mod) =>
23
- ensureContract({ client, bytecode: mod.bytecode, label: `${mod.name} module` })
24
- )
25
- );
26
-
27
- // then start installing modules
28
19
  debug("installing modules:", modules.map((mod) => mod.name).join(", "));
29
20
  const installTxs = await Promise.all(
30
21
  modules.map((mod) =>
@@ -70,5 +61,5 @@ export async function ensureModules({
70
61
  )
71
62
  );
72
63
 
73
- return (await Promise.all([...contractTxs, ...installTxs])).flat();
64
+ return await Promise.all(installTxs);
74
65
  }
@@ -1,12 +1,11 @@
1
1
  import { Client, Transport, Chain, Account, Hex, getAddress } from "viem";
2
2
  import { writeContract } from "@latticexyz/common";
3
3
  import { System, WorldDeploy, worldAbi } from "./common";
4
- import { ensureContract } from "./ensureContract";
5
4
  import { debug } from "./debug";
6
5
  import { resourceLabel } from "./resourceLabel";
7
6
  import { getSystems } from "./getSystems";
8
7
  import { getResourceAccess } from "./getResourceAccess";
9
- import { uniqueBy, wait } from "@latticexyz/common/utils";
8
+ import { wait } from "@latticexyz/common/utils";
10
9
  import pRetry from "p-retry";
11
10
 
12
11
  export async function ensureSystems({
@@ -53,7 +52,7 @@ export async function ensureSystems({
53
52
  debug("adding", accessToAdd.length, "access grants");
54
53
  }
55
54
 
56
- const accessTxs = await Promise.all([
55
+ const accessTxs = [
57
56
  ...accessToRemove.map((access) =>
58
57
  pRetry(
59
58
  () =>
@@ -94,7 +93,7 @@ export async function ensureSystems({
94
93
  }
95
94
  )
96
95
  ),
97
- ]);
96
+ ];
98
97
 
99
98
  const existingSystems = systems.filter((system) =>
100
99
  worldSystems.some(
@@ -127,37 +126,27 @@ export async function ensureSystems({
127
126
  debug("registering new systems", systemsToAdd.map(resourceLabel).join(", "));
128
127
  }
129
128
 
130
- // kick off contract deployments first, otherwise registering systems can fail
131
- const contractTxs = await Promise.all(
132
- uniqueBy(missingSystems, (system) => system.address).map((system) =>
133
- ensureContract({ client, bytecode: system.bytecode, label: `${resourceLabel(system)} system` })
134
- )
135
- );
136
-
137
- // then start registering systems
138
- const registerTxs = await Promise.all(
139
- missingSystems.map((system) =>
140
- pRetry(
141
- () =>
142
- writeContract(client, {
143
- chain: client.chain ?? null,
144
- address: worldDeploy.address,
145
- abi: worldAbi,
146
- // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
147
- functionName: "registerSystem",
148
- args: [system.systemId, system.address, system.allowAll],
149
- }),
150
- {
151
- retries: 3,
152
- onFailedAttempt: async (error) => {
153
- const delay = error.attemptNumber * 500;
154
- debug(`failed to register system ${resourceLabel(system)}, retrying in ${delay}ms...`);
155
- await wait(delay);
156
- },
157
- }
158
- )
129
+ const registerTxs = missingSystems.map((system) =>
130
+ pRetry(
131
+ () =>
132
+ writeContract(client, {
133
+ chain: client.chain ?? null,
134
+ address: worldDeploy.address,
135
+ abi: worldAbi,
136
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
137
+ functionName: "registerSystem",
138
+ args: [system.systemId, system.address, system.allowAll],
139
+ }),
140
+ {
141
+ retries: 3,
142
+ onFailedAttempt: async (error) => {
143
+ const delay = error.attemptNumber * 500;
144
+ debug(`failed to register system ${resourceLabel(system)}, retrying in ${delay}ms...`);
145
+ await wait(delay);
146
+ },
147
+ }
159
148
  )
160
149
  );
161
150
 
162
- return (await Promise.all([...accessTxs, ...contractTxs, ...registerTxs])).flat();
151
+ return await Promise.all([...accessTxs, ...registerTxs]);
163
152
  }
package/src/runDeploy.ts CHANGED
@@ -26,6 +26,10 @@ export const deployOptions = {
26
26
  worldAddress: { type: "string", desc: "Deploy to an existing World at the given address" },
27
27
  srcDir: { type: "string", desc: "Source directory. Defaults to foundry src directory." },
28
28
  skipBuild: { type: "boolean", desc: "Skip rebuilding the contracts before deploying" },
29
+ alwaysRunPostDeploy: {
30
+ type: "boolean",
31
+ 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.",
32
+ },
29
33
  } as const satisfies Record<string, Options>;
30
34
 
31
35
  export type DeployOptions = InferredOptionTypes<typeof deployOptions>;
@@ -84,7 +88,7 @@ in your contracts directory to use the default anvil private key.`
84
88
  client,
85
89
  config: resolvedConfig,
86
90
  });
87
- if (opts.worldAddress == null) {
91
+ if (opts.worldAddress == null || opts.alwaysRunPostDeploy) {
88
92
  await postDeploy(config.postDeployScript, worldDeploy.address, rpc, profile);
89
93
  }
90
94
  console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds"));