@latticexyz/cli 2.0.0-main-e667ee80 → 2.0.0-main-61c6ab70

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-e667ee80",
3
+ "version": "2.0.0-main-61c6ab70",
4
4
  "description": "Command line interface for mud",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,6 +31,7 @@
31
31
  "glob": "^8.0.3",
32
32
  "nice-grpc-web": "^2.0.1",
33
33
  "openurl": "^1.1.1",
34
+ "p-retry": "^5.1.2",
34
35
  "path": "^0.12.7",
35
36
  "rxjs": "7.5.5",
36
37
  "throttle-debounce": "^5.0.0",
@@ -39,17 +40,17 @@
39
40
  "yargs": "^17.7.1",
40
41
  "zod": "^3.21.4",
41
42
  "zod-validation-error": "^1.3.0",
42
- "@latticexyz/abi-ts": "2.0.0-main-e667ee80",
43
- "@latticexyz/common": "2.0.0-main-e667ee80",
44
- "@latticexyz/config": "2.0.0-main-e667ee80",
45
- "@latticexyz/gas-report": "2.0.0-main-e667ee80",
46
- "@latticexyz/protocol-parser": "2.0.0-main-e667ee80",
47
- "@latticexyz/schema-type": "2.0.0-main-e667ee80",
48
- "@latticexyz/services": "2.0.0-main-e667ee80",
49
- "@latticexyz/store": "2.0.0-main-e667ee80",
50
- "@latticexyz/utils": "2.0.0-main-e667ee80",
51
- "@latticexyz/world": "2.0.0-main-e667ee80",
52
- "@latticexyz/world-modules": "2.0.0-main-e667ee80"
43
+ "@latticexyz/abi-ts": "2.0.0-main-61c6ab70",
44
+ "@latticexyz/common": "2.0.0-main-61c6ab70",
45
+ "@latticexyz/config": "2.0.0-main-61c6ab70",
46
+ "@latticexyz/gas-report": "2.0.0-main-61c6ab70",
47
+ "@latticexyz/protocol-parser": "2.0.0-main-61c6ab70",
48
+ "@latticexyz/schema-type": "2.0.0-main-61c6ab70",
49
+ "@latticexyz/services": "2.0.0-main-61c6ab70",
50
+ "@latticexyz/store": "2.0.0-main-61c6ab70",
51
+ "@latticexyz/utils": "2.0.0-main-61c6ab70",
52
+ "@latticexyz/world": "2.0.0-main-61c6ab70",
53
+ "@latticexyz/world-modules": "2.0.0-main-61c6ab70"
53
54
  },
54
55
  "devDependencies": {
55
56
  "@types/debug": "^4.1.7",
@@ -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;
@@ -4,13 +4,16 @@ import { deployWorld } from "./deployWorld";
4
4
  import { ensureTables } from "./ensureTables";
5
5
  import { Config, ConfigInput, WorldDeploy, supportedStoreVersions, supportedWorldVersions } from "./common";
6
6
  import { ensureSystems } from "./ensureSystems";
7
- import { getTransactionReceipt, waitForTransactionReceipt } from "viem/actions";
7
+ import { waitForTransactionReceipt } from "viem/actions";
8
8
  import { getWorldDeploy } from "./getWorldDeploy";
9
9
  import { ensureFunctions } from "./ensureFunctions";
10
10
  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?
@@ -4,6 +4,8 @@ import { deployer } from "./ensureDeployer";
4
4
  import { salt } from "./common";
5
5
  import { sendTransaction } from "@latticexyz/common";
6
6
  import { debug } from "./debug";
7
+ import pRetry from "p-retry";
8
+ import { wait } from "@latticexyz/common/utils";
7
9
 
8
10
  export async function ensureContract({
9
11
  client,
@@ -24,10 +26,21 @@ export async function ensureContract({
24
26
 
25
27
  debug("deploying", label, "at", address);
26
28
  return [
27
- await sendTransaction(client, {
28
- chain: client.chain ?? null,
29
- to: deployer,
30
- data: concatHex([salt, bytecode]),
31
- }),
29
+ await pRetry(
30
+ () =>
31
+ sendTransaction(client, {
32
+ chain: client.chain ?? null,
33
+ to: deployer,
34
+ data: concatHex([salt, bytecode]),
35
+ }),
36
+ {
37
+ retries: 3,
38
+ onFailedAttempt: async (error) => {
39
+ const delay = error.attemptNumber * 500;
40
+ debug(`failed to deploy ${label}, retrying in ${delay}ms...`);
41
+ await wait(delay);
42
+ },
43
+ }
44
+ ),
32
45
  ];
33
46
  }
@@ -3,6 +3,8 @@ import { hexToResource, writeContract } from "@latticexyz/common";
3
3
  import { WorldDeploy, WorldFunction, worldAbi } from "./common";
4
4
  import { debug } from "./debug";
5
5
  import { getFunctions } from "./getFunctions";
6
+ import pRetry from "p-retry";
7
+ import { wait } from "@latticexyz/common/utils";
6
8
 
7
9
  export async function ensureFunctions({
8
10
  client,
@@ -40,23 +42,45 @@ export async function ensureFunctions({
40
42
  toAdd.map((func) => {
41
43
  const { namespace } = hexToResource(func.systemId);
42
44
  if (namespace === "") {
43
- return writeContract(client, {
44
- chain: client.chain ?? null,
45
- address: worldDeploy.address,
46
- abi: worldAbi,
47
- // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
48
- functionName: "registerRootFunctionSelector",
49
- args: [func.systemId, func.systemFunctionSignature, func.systemFunctionSelector],
50
- });
45
+ return pRetry(
46
+ () =>
47
+ writeContract(client, {
48
+ chain: client.chain ?? null,
49
+ address: worldDeploy.address,
50
+ abi: worldAbi,
51
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
52
+ functionName: "registerRootFunctionSelector",
53
+ args: [func.systemId, func.systemFunctionSignature, func.systemFunctionSelector],
54
+ }),
55
+ {
56
+ retries: 3,
57
+ onFailedAttempt: async (error) => {
58
+ const delay = error.attemptNumber * 500;
59
+ debug(`failed to register function ${func.signature}, retrying in ${delay}ms...`);
60
+ await wait(delay);
61
+ },
62
+ }
63
+ );
51
64
  }
52
- return writeContract(client, {
53
- chain: client.chain ?? null,
54
- address: worldDeploy.address,
55
- abi: worldAbi,
56
- // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
57
- functionName: "registerFunctionSelector",
58
- args: [func.systemId, func.systemFunctionSignature],
59
- });
65
+ return pRetry(
66
+ () =>
67
+ writeContract(client, {
68
+ chain: client.chain ?? null,
69
+ address: worldDeploy.address,
70
+ abi: worldAbi,
71
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
72
+ functionName: "registerFunctionSelector",
73
+ args: [func.systemId, func.systemFunctionSignature],
74
+ }),
75
+ {
76
+ retries: 3,
77
+ onFailedAttempt: async (error) => {
78
+ const delay = error.attemptNumber * 500;
79
+ debug(`failed to register function ${func.signature}, retrying in ${delay}ms...`);
80
+ await wait(delay);
81
+ },
82
+ }
83
+ );
60
84
  })
61
85
  );
62
86
  }
@@ -1,9 +1,9 @@
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 } from "@latticexyz/common/utils";
5
+ import { wait } from "@latticexyz/common/utils";
6
+ import pRetry from "p-retry";
7
7
 
8
8
  export async function ensureModules({
9
9
  client,
@@ -16,36 +16,50 @@ export async function ensureModules({
16
16
  }): Promise<readonly Hex[]> {
17
17
  if (!modules.length) return [];
18
18
 
19
- // kick off contract deployments first, otherwise installing modules can fail
20
- const contractTxs = await Promise.all(
21
- uniqueBy(modules, (mod) => mod.address).map((mod) =>
22
- ensureContract({ client, bytecode: mod.bytecode, label: `${mod.name} module` })
23
- )
24
- );
25
-
26
- // then start installing modules
27
19
  debug("installing modules:", modules.map((mod) => mod.name).join(", "));
28
20
  const installTxs = await Promise.all(
29
21
  modules.map((mod) =>
30
22
  mod.installAsRoot
31
- ? writeContract(client, {
32
- chain: client.chain ?? null,
33
- address: worldDeploy.address,
34
- abi: worldAbi,
35
- // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
36
- functionName: "installRootModule",
37
- args: [mod.address, mod.installData],
38
- })
39
- : writeContract(client, {
40
- chain: client.chain ?? null,
41
- address: worldDeploy.address,
42
- abi: worldAbi,
43
- // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
44
- functionName: "installModule",
45
- args: [mod.address, mod.installData],
46
- })
23
+ ? pRetry(
24
+ () =>
25
+ writeContract(client, {
26
+ chain: client.chain ?? null,
27
+ address: worldDeploy.address,
28
+ abi: worldAbi,
29
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
30
+ functionName: "installRootModule",
31
+ args: [mod.address, mod.installData],
32
+ }),
33
+ {
34
+ retries: 3,
35
+ onFailedAttempt: async (error) => {
36
+ const delay = error.attemptNumber * 500;
37
+ debug(`failed to install root module ${mod.name}, retrying in ${delay}ms...`);
38
+ await wait(delay);
39
+ },
40
+ }
41
+ )
42
+ : pRetry(
43
+ () =>
44
+ writeContract(client, {
45
+ chain: client.chain ?? null,
46
+ address: worldDeploy.address,
47
+ abi: worldAbi,
48
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
49
+ functionName: "installModule",
50
+ args: [mod.address, mod.installData],
51
+ }),
52
+ {
53
+ retries: 3,
54
+ onFailedAttempt: async (error) => {
55
+ const delay = error.attemptNumber * 500;
56
+ debug(`failed to install module ${mod.name}, retrying in ${delay}ms...`);
57
+ await wait(delay);
58
+ },
59
+ }
60
+ )
47
61
  )
48
62
  );
49
63
 
50
- return (await Promise.all([...contractTxs, ...installTxs])).flat();
64
+ return await Promise.all(installTxs);
51
65
  }
@@ -1,12 +1,12 @@
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 } from "@latticexyz/common/utils";
8
+ import { wait } from "@latticexyz/common/utils";
9
+ import pRetry from "p-retry";
10
10
 
11
11
  export async function ensureSystems({
12
12
  client,
@@ -52,26 +52,48 @@ export async function ensureSystems({
52
52
  debug("adding", accessToAdd.length, "access grants");
53
53
  }
54
54
 
55
- const accessTxs = await Promise.all([
55
+ const accessTxs = [
56
56
  ...accessToRemove.map((access) =>
57
- writeContract(client, {
58
- chain: client.chain ?? null,
59
- address: worldDeploy.address,
60
- abi: worldAbi,
61
- functionName: "revokeAccess",
62
- args: [access.resourceId, access.address],
63
- })
57
+ pRetry(
58
+ () =>
59
+ writeContract(client, {
60
+ chain: client.chain ?? null,
61
+ address: worldDeploy.address,
62
+ abi: worldAbi,
63
+ functionName: "revokeAccess",
64
+ args: [access.resourceId, access.address],
65
+ }),
66
+ {
67
+ retries: 3,
68
+ onFailedAttempt: async (error) => {
69
+ const delay = error.attemptNumber * 500;
70
+ debug(`failed to revoke access, retrying in ${delay}ms...`);
71
+ await wait(delay);
72
+ },
73
+ }
74
+ )
64
75
  ),
65
76
  ...accessToAdd.map((access) =>
66
- writeContract(client, {
67
- chain: client.chain ?? null,
68
- address: worldDeploy.address,
69
- abi: worldAbi,
70
- functionName: "grantAccess",
71
- args: [access.resourceId, access.address],
72
- })
77
+ pRetry(
78
+ () =>
79
+ writeContract(client, {
80
+ chain: client.chain ?? null,
81
+ address: worldDeploy.address,
82
+ abi: worldAbi,
83
+ functionName: "grantAccess",
84
+ args: [access.resourceId, access.address],
85
+ }),
86
+ {
87
+ retries: 3,
88
+ onFailedAttempt: async (error) => {
89
+ const delay = error.attemptNumber * 500;
90
+ debug(`failed to grant access, retrying in ${delay}ms...`);
91
+ await wait(delay);
92
+ },
93
+ }
94
+ )
73
95
  ),
74
- ]);
96
+ ];
75
97
 
76
98
  const existingSystems = systems.filter((system) =>
77
99
  worldSystems.some(
@@ -104,26 +126,27 @@ export async function ensureSystems({
104
126
  debug("registering new systems", systemsToAdd.map(resourceLabel).join(", "));
105
127
  }
106
128
 
107
- // kick off contract deployments first, otherwise registering systems can fail
108
- const contractTxs = await Promise.all(
109
- uniqueBy(missingSystems, (system) => system.address).map((system) =>
110
- ensureContract({ client, bytecode: system.bytecode, label: `${resourceLabel(system)} system` })
111
- )
112
- );
113
-
114
- // then start registering systems
115
- const registerTxs = await Promise.all(
116
- missingSystems.map((system) =>
117
- writeContract(client, {
118
- chain: client.chain ?? null,
119
- address: worldDeploy.address,
120
- abi: worldAbi,
121
- // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
122
- functionName: "registerSystem",
123
- args: [system.systemId, system.address, system.allowAll],
124
- })
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
+ }
125
148
  )
126
149
  );
127
150
 
128
- return (await Promise.all([...accessTxs, ...contractTxs, ...registerTxs])).flat();
151
+ return await Promise.all([...accessTxs, ...registerTxs]);
129
152
  }
@@ -6,6 +6,8 @@ import { valueSchemaToFieldLayoutHex, keySchemaToHex, valueSchemaToHex } from "@
6
6
  import { debug } from "./debug";
7
7
  import { resourceLabel } from "./resourceLabel";
8
8
  import { getTables } from "./getTables";
9
+ import pRetry from "p-retry";
10
+ import { wait } from "@latticexyz/common/utils";
9
11
 
10
12
  export async function ensureTables({
11
13
  client,
@@ -29,21 +31,32 @@ export async function ensureTables({
29
31
  debug("registering tables", missingTables.map(resourceLabel).join(", "));
30
32
  return await Promise.all(
31
33
  missingTables.map((table) =>
32
- writeContract(client, {
33
- chain: client.chain ?? null,
34
- address: worldDeploy.address,
35
- abi: worldAbi,
36
- // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
37
- functionName: "registerTable",
38
- args: [
39
- table.tableId,
40
- valueSchemaToFieldLayoutHex(table.valueSchema),
41
- keySchemaToHex(table.keySchema),
42
- valueSchemaToHex(table.valueSchema),
43
- Object.keys(table.keySchema),
44
- Object.keys(table.valueSchema),
45
- ],
46
- })
34
+ pRetry(
35
+ () =>
36
+ writeContract(client, {
37
+ chain: client.chain ?? null,
38
+ address: worldDeploy.address,
39
+ abi: worldAbi,
40
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
41
+ functionName: "registerTable",
42
+ args: [
43
+ table.tableId,
44
+ valueSchemaToFieldLayoutHex(table.valueSchema),
45
+ keySchemaToHex(table.keySchema),
46
+ valueSchemaToHex(table.valueSchema),
47
+ Object.keys(table.keySchema),
48
+ Object.keys(table.valueSchema),
49
+ ],
50
+ }),
51
+ {
52
+ retries: 3,
53
+ onFailedAttempt: async (error) => {
54
+ const delay = error.attemptNumber * 500;
55
+ debug(`failed to register table ${resourceLabel(table)}, retrying in ${delay}ms...`);
56
+ await wait(delay);
57
+ },
58
+ }
59
+ )
47
60
  )
48
61
  );
49
62
  }
package/src/runDeploy.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { InferredOptionTypes, Options } from "yargs";
4
4
  import { deploy } from "./deploy/deploy";
5
5
  import { createWalletClient, http, Hex } from "viem";