@latticexyz/cli 2.0.0-main-711f6e2a → 2.0.0-march-19-skystrife-playtest-f486281e

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 (45) hide show
  1. package/dist/commands-RZOPG5RM.js +27 -0
  2. package/dist/commands-RZOPG5RM.js.map +1 -0
  3. package/dist/mud.js +1 -1
  4. package/dist/mud.js.map +1 -1
  5. package/package.json +15 -17
  6. package/src/commands/deploy.ts +1 -1
  7. package/src/commands/dev-contracts.ts +1 -4
  8. package/src/commands/set-version.ts +1 -1
  9. package/src/commands/trace.ts +1 -1
  10. package/src/deploy/assertNamespaceOwner.ts +42 -0
  11. package/src/deploy/common.ts +2 -44
  12. package/src/deploy/configToTables.ts +3 -5
  13. package/src/deploy/create2/README.md +0 -4
  14. package/src/deploy/create2/deployment.json +1 -2
  15. package/src/deploy/deploy.ts +18 -43
  16. package/src/deploy/deployWorld.ts +4 -9
  17. package/src/deploy/ensureContract.ts +7 -12
  18. package/src/deploy/ensureContractsDeployed.ts +1 -9
  19. package/src/deploy/ensureDeployer.ts +22 -61
  20. package/src/deploy/ensureFunctions.ts +5 -5
  21. package/src/deploy/ensureModules.ts +10 -16
  22. package/src/deploy/ensureSystems.ts +83 -108
  23. package/src/deploy/ensureTables.ts +8 -7
  24. package/src/deploy/ensureWorldFactory.ts +37 -95
  25. package/src/deploy/getFunctions.ts +4 -4
  26. package/src/deploy/getResourceAccess.ts +2 -2
  27. package/src/deploy/getSystems.ts +7 -6
  28. package/src/deploy/getTables.ts +1 -1
  29. package/src/deploy/logsToWorldDeploy.ts +1 -1
  30. package/src/deploy/resolveConfig.ts +55 -52
  31. package/src/deploy/resourceLabel.ts +3 -0
  32. package/src/mud.ts +1 -1
  33. package/src/mudPackages.ts +1 -1
  34. package/src/runDeploy.ts +7 -34
  35. package/src/utils/{defaultModuleContracts.ts → modules/constants.ts} +0 -4
  36. package/src/utils/printMUD.ts +1 -1
  37. package/src/utils/{getContractData.ts → utils/getContractData.ts} +5 -11
  38. package/src/utils/{postDeploy.ts → utils/postDeploy.ts} +2 -2
  39. package/dist/commands-22M65L4E.js +0 -36
  40. package/dist/commands-22M65L4E.js.map +0 -1
  41. package/src/deploy/createPrepareDeploy.ts +0 -28
  42. package/src/deploy/ensureNamespaceOwner.ts +0 -71
  43. package/src/deploy/findLibraries.ts +0 -36
  44. package/src/deploy/orderByDependencies.ts +0 -12
  45. package/src/utils/findPlaceholders.ts +0 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latticexyz/cli",
3
- "version": "2.0.0-main-711f6e2a",
3
+ "version": "2.0.0-march-19-skystrife-playtest-f486281e",
4
4
  "description": "Command line interface for mud",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,23 +35,22 @@
35
35
  "path": "^0.12.7",
36
36
  "rxjs": "7.5.5",
37
37
  "throttle-debounce": "^5.0.0",
38
- "toposort": "^2.0.2",
39
- "typescript": "5.4.2",
40
- "viem": "2.7.12",
38
+ "typescript": "5.1.6",
39
+ "viem": "1.14.0",
41
40
  "yargs": "^17.7.1",
42
41
  "zod": "^3.21.4",
43
42
  "zod-validation-error": "^1.3.0",
44
- "@latticexyz/abi-ts": "2.0.0-main-711f6e2a",
45
- "@latticexyz/common": "2.0.0-main-711f6e2a",
46
- "@latticexyz/config": "2.0.0-main-711f6e2a",
47
- "@latticexyz/gas-report": "2.0.0-main-711f6e2a",
48
- "@latticexyz/protocol-parser": "2.0.0-main-711f6e2a",
49
- "@latticexyz/schema-type": "2.0.0-main-711f6e2a",
50
- "@latticexyz/services": "2.0.0-main-711f6e2a",
51
- "@latticexyz/store": "2.0.0-main-711f6e2a",
52
- "@latticexyz/utils": "2.0.0-main-711f6e2a",
53
- "@latticexyz/world": "2.0.0-main-711f6e2a",
54
- "@latticexyz/world-modules": "2.0.0-main-711f6e2a"
43
+ "@latticexyz/abi-ts": "2.0.0-march-19-skystrife-playtest-f486281e",
44
+ "@latticexyz/common": "2.0.0-march-19-skystrife-playtest-f486281e",
45
+ "@latticexyz/config": "2.0.0-march-19-skystrife-playtest-f486281e",
46
+ "@latticexyz/gas-report": "2.0.0-march-19-skystrife-playtest-f486281e",
47
+ "@latticexyz/protocol-parser": "2.0.0-march-19-skystrife-playtest-f486281e",
48
+ "@latticexyz/schema-type": "2.0.0-march-19-skystrife-playtest-f486281e",
49
+ "@latticexyz/services": "2.0.0-march-19-skystrife-playtest-f486281e",
50
+ "@latticexyz/store": "2.0.0-march-19-skystrife-playtest-f486281e",
51
+ "@latticexyz/utils": "2.0.0-march-19-skystrife-playtest-f486281e",
52
+ "@latticexyz/world": "2.0.0-march-19-skystrife-playtest-f486281e",
53
+ "@latticexyz/world-modules": "2.0.0-march-19-skystrife-playtest-f486281e"
55
54
  },
56
55
  "devDependencies": {
57
56
  "@types/debug": "^4.1.7",
@@ -60,13 +59,12 @@
60
59
  "@types/node": "^18.15.11",
61
60
  "@types/openurl": "^1.0.0",
62
61
  "@types/throttle-debounce": "^5.0.0",
63
- "@types/toposort": "^2.0.6",
64
62
  "@types/yargs": "^17.0.10",
65
63
  "ds-test": "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0",
66
64
  "forge-std": "https://github.com/foundry-rs/forge-std.git#74cfb77e308dd188d2f58864aaf44963ae6b88b1",
67
65
  "tsup": "^6.7.0",
68
66
  "tsx": "^3.12.6",
69
- "vitest": "0.34.6"
67
+ "vitest": "0.31.4"
70
68
  },
71
69
  "gitHead": "914a1e0ae4a573d685841ca2ea921435057deb8f",
72
70
  "scripts": {
@@ -15,7 +15,7 @@ const commandModule: CommandModule<typeof deployOptions, DeployOptions> = {
15
15
  // Wrap in try/catch, because yargs seems to swallow errors
16
16
  try {
17
17
  await runDeploy(opts);
18
- } catch (error) {
18
+ } catch (error: any) {
19
19
  logError(error);
20
20
  process.exit(1);
21
21
  }
@@ -83,15 +83,12 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
83
83
  ...opts,
84
84
  configPath,
85
85
  rpc,
86
- rpcBatch: false,
87
86
  skipBuild: false,
88
87
  printConfig: false,
89
88
  profile: undefined,
90
89
  saveDeployment: true,
91
- deployerAddress: undefined,
92
90
  worldAddress,
93
91
  srcDir,
94
- salt: "0x",
95
92
  });
96
93
  worldAddress = deploy.address;
97
94
  // if there were changes while we were deploying, trigger it again
@@ -107,7 +104,7 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
107
104
  console.log(chalk.gray("\nWaiting for file changes…\n"));
108
105
  }
109
106
  }),
110
- filter(isDefined),
107
+ filter(isDefined)
111
108
  );
112
109
 
113
110
  deploys$.subscribe();
@@ -45,7 +45,7 @@ const commandModule: CommandModule<Options, Options> = {
45
45
  const mutuallyExclusiveOptions = ["mudVersion", "link", "tag", "commit", "restore"];
46
46
  const numMutuallyExclusiveOptions = mutuallyExclusiveOptions.reduce(
47
47
  (acc, opt) => (options[opt] ? acc + 1 : acc),
48
- 0,
48
+ 0
49
49
  );
50
50
 
51
51
  if (numMutuallyExclusiveOptions === 0) {
@@ -63,7 +63,7 @@ const commandModule: CommandModule<Options, Options> = {
63
63
 
64
64
  const resolvedConfig = resolveWorldConfig(
65
65
  mudConfig,
66
- existingContracts.map(({ basename }) => basename),
66
+ existingContracts.map(({ basename }) => basename)
67
67
  );
68
68
 
69
69
  // Get worldAddress either from args or from worldsFile
@@ -0,0 +1,42 @@
1
+ import { Account, Chain, Client, Hex, Transport, getAddress } from "viem";
2
+ import { WorldDeploy, worldTables } from "./common";
3
+ import { hexToResource, resourceToHex } from "@latticexyz/common";
4
+ import { getResourceIds } from "./getResourceIds";
5
+ import { getTableValue } from "./getTableValue";
6
+
7
+ export async function assertNamespaceOwner({
8
+ client,
9
+ worldDeploy,
10
+ resourceIds,
11
+ }: {
12
+ readonly client: Client<Transport, Chain | undefined, Account>;
13
+ readonly worldDeploy: WorldDeploy;
14
+ readonly resourceIds: readonly Hex[];
15
+ }): Promise<void> {
16
+ const desiredNamespaces = Array.from(new Set(resourceIds.map((resourceId) => hexToResource(resourceId).namespace)));
17
+ const existingResourceIds = await getResourceIds({ client, worldDeploy });
18
+ const existingNamespaces = Array.from(
19
+ new Set(existingResourceIds.map((resourceId) => hexToResource(resourceId).namespace))
20
+ );
21
+
22
+ const namespaces = desiredNamespaces.filter((namespace) => existingNamespaces.includes(namespace));
23
+ const namespaceOwners = await Promise.all(
24
+ namespaces.map(async (namespace) => {
25
+ const { owner } = await getTableValue({
26
+ client,
27
+ worldDeploy,
28
+ table: worldTables.world_NamespaceOwner,
29
+ key: { namespaceId: resourceToHex({ type: "namespace", namespace, name: "" }) },
30
+ });
31
+ return [namespace, owner];
32
+ })
33
+ );
34
+
35
+ const unauthorizedNamespaces = namespaceOwners
36
+ .filter(([, owner]) => getAddress(owner) !== getAddress(client.account.address))
37
+ .map(([namespace]) => namespace);
38
+
39
+ if (unauthorizedNamespaces.length) {
40
+ throw new Error(`You are attempting to deploy to namespaces you do not own: ${unauthorizedNamespaces.join(", ")}`);
41
+ }
42
+ }
@@ -24,7 +24,6 @@ export const worldAbi = [...IBaseWorldAbi, ...IModuleAbi] as const;
24
24
  export const supportedStoreVersions = ["1.0.0-unaudited"];
25
25
  export const supportedWorldVersions = ["1.0.0-unaudited"];
26
26
 
27
- // TODO: extend this to include factory+deployer address? so we can reuse the deployer for a world?
28
27
  export type WorldDeploy = {
29
28
  readonly address: Address;
30
29
  readonly worldVersion: string;
@@ -47,62 +46,22 @@ export type WorldFunction = {
47
46
  readonly systemFunctionSelector: Hex;
48
47
  };
49
48
 
50
- export type LibraryPlaceholder = {
51
- /**
52
- * Path to library source file, e.g. `src/libraries/SomeLib.sol`
53
- */
54
- path: string;
55
- /**
56
- * Library name, e.g. `SomeLib`
57
- */
58
- name: string;
59
- /**
60
- * Byte offset of placeholder in bytecode
61
- */
62
- start: number;
63
- /**
64
- * Size of placeholder to replace in bytes
65
- */
66
- length: number;
67
- };
68
-
69
49
  export type DeterministicContract = {
70
- readonly prepareDeploy: (
71
- deployer: Address,
72
- libraries: readonly Library[],
73
- ) => {
74
- readonly address: Address;
75
- readonly bytecode: Hex;
76
- };
50
+ readonly address: Address;
51
+ readonly bytecode: Hex;
77
52
  readonly deployedBytecodeSize: number;
78
53
  readonly abi: Abi;
79
54
  };
80
55
 
81
- export type Library = DeterministicContract & {
82
- /**
83
- * Path to library source file, e.g. `src/libraries/SomeLib.sol`
84
- */
85
- path: string;
86
- /**
87
- * Library name, e.g. `SomeLib`
88
- */
89
- name: string;
90
- };
91
-
92
56
  export type System = DeterministicContract & {
93
57
  readonly namespace: string;
94
58
  readonly name: string;
95
59
  readonly systemId: Hex;
96
60
  readonly allowAll: boolean;
97
61
  readonly allowedAddresses: readonly Hex[];
98
- readonly allowedSystemIds: readonly Hex[];
99
62
  readonly functions: readonly WorldFunction[];
100
63
  };
101
64
 
102
- export type DeployedSystem = Omit<System, "abi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds"> & {
103
- address: Address;
104
- };
105
-
106
65
  export type Module = DeterministicContract & {
107
66
  readonly name: string;
108
67
  readonly installAsRoot: boolean;
@@ -114,5 +73,4 @@ export type Config<config extends ConfigInput> = {
114
73
  readonly tables: Tables<config>;
115
74
  readonly systems: readonly System[];
116
75
  readonly modules: readonly Module[];
117
- readonly libraries: readonly Library[];
118
76
  };
@@ -14,12 +14,12 @@ type UserTypes<config extends StoreConfig = StoreConfig> = config["userTypes"];
14
14
 
15
15
  export type TableKey<
16
16
  config extends StoreConfig = StoreConfig,
17
- table extends config["tables"][keyof config["tables"]] = config["tables"][keyof config["tables"]],
17
+ table extends config["tables"][keyof config["tables"]] = config["tables"][keyof config["tables"]]
18
18
  > = `${config["namespace"]}_${table["name"]}`;
19
19
 
20
20
  export type Table<
21
21
  config extends StoreConfig = StoreConfig,
22
- table extends config["tables"][keyof config["tables"]] = config["tables"][keyof config["tables"]],
22
+ table extends config["tables"][keyof config["tables"]] = config["tables"][keyof config["tables"]]
23
23
  > = {
24
24
  readonly namespace: config["namespace"];
25
25
  readonly name: table["name"];
@@ -60,11 +60,9 @@ export function configToTables<config extends StoreConfig>(config: config): Tabl
60
60
  namespace: config.namespace,
61
61
  name: table.name,
62
62
  }),
63
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
63
  keySchema: resolveUserTypes(table.keySchema, userTypes) as any,
65
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
64
  valueSchema: resolveUserTypes(table.valueSchema, userTypes) as any,
67
65
  } satisfies Table<config, config["tables"][keyof config["tables"]]>,
68
- ]),
66
+ ])
69
67
  ) as Tables<config>;
70
68
  }
@@ -6,8 +6,4 @@ cd deterministic-deployment-proxy
6
6
  git checkout b3bb19c
7
7
  npm install
8
8
  npm run build
9
- cd output
10
- jq --arg bc "$(cat bytecode.txt)" '. + {bytecode: $bc}' deployment.json > deployment-with-bytecode.json
11
- mv deployment-with-bytecode.json deployment.json
12
- cp deployment.json ../path/to/this/dir
13
9
  ```
@@ -3,6 +3,5 @@
3
3
  "gasLimit": 100000,
4
4
  "signerAddress": "3fab184622dc19b6109349b94811493bf2a45362",
5
5
  "transaction": "f8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222",
6
- "address": "4e59b44847b379578588920ca78fbf26c0b4956c",
7
- "bytecode": "604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"
6
+ "address": "4e59b44847b379578588920ca78fbf26c0b4956c"
8
7
  }
@@ -1,4 +1,4 @@
1
- import { Account, Address, Chain, Client, Hex, Transport } from "viem";
1
+ import { Account, Address, Chain, Client, Transport, getAddress } from "viem";
2
2
  import { ensureDeployer } from "./ensureDeployer";
3
3
  import { deployWorld } from "./deployWorld";
4
4
  import { ensureTables } from "./ensureTables";
@@ -9,25 +9,17 @@ import { getWorldDeploy } from "./getWorldDeploy";
9
9
  import { ensureFunctions } from "./ensureFunctions";
10
10
  import { ensureModules } from "./ensureModules";
11
11
  import { Table } from "./configToTables";
12
- import { ensureNamespaceOwner } from "./ensureNamespaceOwner";
12
+ import { assertNamespaceOwner } from "./assertNamespaceOwner";
13
13
  import { debug } from "./debug";
14
- import { resourceToLabel } from "@latticexyz/common";
14
+ import { resourceLabel } from "./resourceLabel";
15
+ import { uniqueBy } from "@latticexyz/common/utils";
15
16
  import { ensureContractsDeployed } from "./ensureContractsDeployed";
16
- import { randomBytes } from "crypto";
17
- import { ensureWorldFactory } from "./ensureWorldFactory";
17
+ import { coreModuleBytecode, worldFactoryBytecode, worldFactoryContracts } from "./ensureWorldFactory";
18
18
 
19
19
  type DeployOptions<configInput extends ConfigInput> = {
20
20
  client: Client<Transport, Chain | undefined, Account>;
21
21
  config: Config<configInput>;
22
- salt?: Hex;
23
22
  worldAddress?: Address;
24
- /**
25
- * Address of determinstic deployment proxy: https://github.com/Arachnid/deterministic-deployment-proxy
26
- * By default, we look for a deployment at 0x4e59b44847b379578588920ca78fbf26c0b4956c and, if not, deploy one.
27
- * If the target chain does not support legacy transactions, we deploy the proxy bytecode anyway, but it will
28
- * not have a deterministic address.
29
- */
30
- deployerAddress?: Hex;
31
23
  };
32
24
 
33
25
  /**
@@ -39,33 +31,25 @@ type DeployOptions<configInput extends ConfigInput> = {
39
31
  export async function deploy<configInput extends ConfigInput>({
40
32
  client,
41
33
  config,
42
- salt,
43
34
  worldAddress: existingWorldAddress,
44
- deployerAddress: initialDeployerAddress,
45
35
  }: DeployOptions<configInput>): Promise<WorldDeploy> {
46
36
  const tables = Object.values(config.tables) as Table[];
37
+ const systems = Object.values(config.systems);
47
38
 
48
- const deployerAddress = initialDeployerAddress ?? (await ensureDeployer(client));
49
-
50
- await ensureWorldFactory(client, deployerAddress);
39
+ await ensureDeployer(client);
51
40
 
52
41
  // deploy all dependent contracts, because system registration, module install, etc. all expect these contracts to be callable.
53
42
  await ensureContractsDeployed({
54
43
  client,
55
- deployerAddress,
56
44
  contracts: [
57
- ...config.libraries.map((library) => ({
58
- bytecode: library.prepareDeploy(deployerAddress, config.libraries).bytecode,
59
- deployedBytecodeSize: library.deployedBytecodeSize,
60
- label: `${library.path}:${library.name} library`,
61
- })),
62
- ...config.systems.map((system) => ({
63
- bytecode: system.prepareDeploy(deployerAddress, config.libraries).bytecode,
45
+ ...worldFactoryContracts,
46
+ ...uniqueBy(systems, (system) => getAddress(system.address)).map((system) => ({
47
+ bytecode: system.bytecode,
64
48
  deployedBytecodeSize: system.deployedBytecodeSize,
65
- label: `${resourceToLabel(system)} system`,
49
+ label: `${resourceLabel(system)} system`,
66
50
  })),
67
- ...config.modules.map((mod) => ({
68
- bytecode: mod.prepareDeploy(deployerAddress, config.libraries).bytecode,
51
+ ...uniqueBy(config.modules, (mod) => getAddress(mod.address)).map((mod) => ({
52
+ bytecode: mod.bytecode,
69
53
  deployedBytecodeSize: mod.deployedBytecodeSize,
70
54
  label: `${mod.name} module`,
71
55
  })),
@@ -74,7 +58,7 @@ export async function deploy<configInput extends ConfigInput>({
74
58
 
75
59
  const worldDeploy = existingWorldAddress
76
60
  ? await getWorldDeploy(client, existingWorldAddress)
77
- : await deployWorld(client, deployerAddress, salt ?? `0x${randomBytes(32).toString("hex")}`);
61
+ : await deployWorld(client);
78
62
 
79
63
  if (!supportedStoreVersions.includes(worldDeploy.storeVersion)) {
80
64
  throw new Error(`Unsupported Store version: ${worldDeploy.storeVersion}`);
@@ -83,17 +67,12 @@ export async function deploy<configInput extends ConfigInput>({
83
67
  throw new Error(`Unsupported World version: ${worldDeploy.worldVersion}`);
84
68
  }
85
69
 
86
- const namespaceTxs = await ensureNamespaceOwner({
70
+ await assertNamespaceOwner({
87
71
  client,
88
72
  worldDeploy,
89
- resourceIds: [...tables.map((table) => table.tableId), ...config.systems.map((system) => system.systemId)],
73
+ resourceIds: [...tables.map((table) => table.tableId), ...systems.map((system) => system.systemId)],
90
74
  });
91
75
 
92
- debug("waiting for all namespace registration transactions to confirm");
93
- for (const tx of namespaceTxs) {
94
- await waitForTransactionReceipt(client, { hash: tx });
95
- }
96
-
97
76
  const tableTxs = await ensureTables({
98
77
  client,
99
78
  worldDeploy,
@@ -101,20 +80,16 @@ export async function deploy<configInput extends ConfigInput>({
101
80
  });
102
81
  const systemTxs = await ensureSystems({
103
82
  client,
104
- deployerAddress,
105
- libraries: config.libraries,
106
83
  worldDeploy,
107
- systems: config.systems,
84
+ systems,
108
85
  });
109
86
  const functionTxs = await ensureFunctions({
110
87
  client,
111
88
  worldDeploy,
112
- functions: config.systems.flatMap((system) => system.functions),
89
+ functions: systems.flatMap((system) => system.functions),
113
90
  });
114
91
  const moduleTxs = await ensureModules({
115
92
  client,
116
- deployerAddress,
117
- libraries: config.libraries,
118
93
  worldDeploy,
119
94
  modules: config.modules,
120
95
  });
@@ -1,18 +1,14 @@
1
- import { Account, Chain, Client, Hex, Log, Transport } from "viem";
1
+ import { Account, Chain, Client, Log, Transport } from "viem";
2
2
  import { waitForTransactionReceipt } from "viem/actions";
3
- import { ensureWorldFactory } from "./ensureWorldFactory";
3
+ import { ensureWorldFactory, worldFactory } from "./ensureWorldFactory";
4
4
  import WorldFactoryAbi from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.abi.json" assert { type: "json" };
5
5
  import { writeContract } from "@latticexyz/common";
6
6
  import { debug } from "./debug";
7
7
  import { logsToWorldDeploy } from "./logsToWorldDeploy";
8
8
  import { WorldDeploy } from "./common";
9
9
 
10
- export async function deployWorld(
11
- client: Client<Transport, Chain | undefined, Account>,
12
- deployerAddress: Hex,
13
- salt: Hex,
14
- ): Promise<WorldDeploy> {
15
- const worldFactory = await ensureWorldFactory(client, deployerAddress);
10
+ export async function deployWorld(client: Client<Transport, Chain | undefined, Account>): Promise<WorldDeploy> {
11
+ await ensureWorldFactory(client);
16
12
 
17
13
  debug("deploying world");
18
14
  const tx = await writeContract(client, {
@@ -20,7 +16,6 @@ export async function deployWorld(
20
16
  address: worldFactory,
21
17
  abi: WorldFactoryAbi,
22
18
  functionName: "deployWorld",
23
- args: [salt],
24
19
  });
25
20
 
26
21
  debug("waiting for world deploy");
@@ -1,5 +1,6 @@
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
+ import { deployer } from "./ensureDeployer";
3
4
  import { contractSizeLimit, salt } from "./common";
4
5
  import { sendTransaction } from "@latticexyz/common";
5
6
  import { debug } from "./debug";
@@ -14,19 +15,13 @@ export type Contract = {
14
15
 
15
16
  export async function ensureContract({
16
17
  client,
17
- deployerAddress,
18
18
  bytecode,
19
19
  deployedBytecodeSize,
20
20
  label = "contract",
21
21
  }: {
22
22
  readonly client: Client<Transport, Chain | undefined, Account>;
23
- readonly deployerAddress: Hex;
24
23
  } & Contract): Promise<readonly Hex[]> {
25
- if (bytecode.includes("__$")) {
26
- throw new Error(`Found unlinked public library in ${label} bytecode`);
27
- }
28
-
29
- const address = getCreate2Address({ from: deployerAddress, salt, bytecode });
24
+ const address = getCreate2Address({ from: deployer, salt, bytecode });
30
25
 
31
26
  const contractCode = await getBytecode(client, { address, blockTag: "pending" });
32
27
  if (contractCode) {
@@ -36,11 +31,11 @@ export async function ensureContract({
36
31
 
37
32
  if (deployedBytecodeSize > contractSizeLimit) {
38
33
  console.warn(
39
- `\nBytecode for ${label} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
34
+ `\nBytecode for ${label} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`
40
35
  );
41
36
  } else if (deployedBytecodeSize > contractSizeLimit * 0.95) {
42
37
  console.warn(
43
- `\nBytecode for ${label} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
38
+ `\nBytecode for ${label} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`
44
39
  );
45
40
  }
46
41
 
@@ -50,7 +45,7 @@ export async function ensureContract({
50
45
  () =>
51
46
  sendTransaction(client, {
52
47
  chain: client.chain ?? null,
53
- to: deployerAddress,
48
+ to: deployer,
54
49
  data: concatHex([salt, bytecode]),
55
50
  }),
56
51
  {
@@ -60,7 +55,7 @@ export async function ensureContract({
60
55
  debug(`failed to deploy ${label}, retrying in ${delay}ms...`);
61
56
  await wait(delay);
62
57
  },
63
- },
58
+ }
64
59
  ),
65
60
  ];
66
61
  }
@@ -2,23 +2,15 @@ import { Client, Transport, Chain, Account, Hex } from "viem";
2
2
  import { waitForTransactionReceipt } from "viem/actions";
3
3
  import { debug } from "./debug";
4
4
  import { Contract, ensureContract } from "./ensureContract";
5
- import { uniqueBy } from "@latticexyz/common/utils";
6
5
 
7
6
  export async function ensureContractsDeployed({
8
7
  client,
9
- deployerAddress,
10
8
  contracts,
11
9
  }: {
12
10
  readonly client: Client<Transport, Chain | undefined, Account>;
13
- readonly deployerAddress: Hex;
14
11
  readonly contracts: readonly Contract[];
15
12
  }): Promise<readonly Hex[]> {
16
- // Deployments assume a deterministic deployer, so we only need to deploy the unique bytecode
17
- const uniqueContracts = uniqueBy(contracts, (contract) => contract.bytecode);
18
-
19
- const txs = (
20
- await Promise.all(uniqueContracts.map((contract) => ensureContract({ client, deployerAddress, ...contract })))
21
- ).flat();
13
+ const txs = (await Promise.all(contracts.map((contract) => ensureContract({ client, ...contract })))).flat();
22
14
 
23
15
  if (txs.length) {
24
16
  debug("waiting for contracts");
@@ -1,75 +1,36 @@
1
- import { Account, Address, Chain, Client, Transport } from "viem";
2
- import { getBalance, getBytecode, sendRawTransaction, sendTransaction, waitForTransactionReceipt } from "viem/actions";
1
+ import { Account, Chain, Client, Transport } from "viem";
2
+ import { getBytecode, sendRawTransaction, sendTransaction, waitForTransactionReceipt } from "viem/actions";
3
3
  import deployment from "./create2/deployment.json";
4
4
  import { debug } from "./debug";
5
5
 
6
- const deployer = `0x${deployment.address}` as const;
7
- const deployerBytecode = `0x${deployment.bytecode}` as const;
6
+ export const deployer = `0x${deployment.address}` as const;
8
7
 
9
- export async function ensureDeployer(client: Client<Transport, Chain | undefined, Account>): Promise<Address> {
8
+ export async function ensureDeployer(client: Client<Transport, Chain | undefined, Account>): Promise<void> {
10
9
  const bytecode = await getBytecode(client, { address: deployer });
11
10
  if (bytecode) {
12
- debug("found CREATE2 deployer at", deployer);
13
- if (bytecode !== deployerBytecode) {
14
- console.warn(
15
- `\n ⚠️ Bytecode for deployer at ${deployer} did not match the expected CREATE2 bytecode. You may have unexpected results.\n`,
16
- );
17
- }
18
- return deployer;
11
+ debug("found create2 deployer at", deployer);
12
+ return;
19
13
  }
20
14
 
21
- // There's not really a way to simulate a pre-EIP-155 (no chain ID) transaction,
22
- // so we have to attempt to create the deployer first and, if it fails, fall back
23
- // to a regular deploy.
24
-
25
- // Send gas to deployment signer
26
- const gasRequired = BigInt(deployment.gasLimit) * BigInt(deployment.gasPrice);
27
- const currentBalance = await getBalance(client, { address: `0x${deployment.signerAddress}` });
28
- const gasNeeded = gasRequired - currentBalance;
29
- if (gasNeeded > 0) {
30
- debug("sending gas for CREATE2 deployer to signer at", deployment.signerAddress);
31
- const gasTx = await sendTransaction(client, {
32
- chain: client.chain ?? null,
33
- to: `0x${deployment.signerAddress}`,
34
- value: gasNeeded,
35
- });
36
- const gasReceipt = await waitForTransactionReceipt(client, { hash: gasTx });
37
- if (gasReceipt.status !== "success") {
38
- console.error("failed to send gas to deployer signer", gasReceipt);
39
- throw new Error("failed to send gas to deployer signer");
40
- }
15
+ // send gas to signer
16
+ debug("sending gas for create2 deployer to signer at", deployment.signerAddress);
17
+ const gasTx = await sendTransaction(client, {
18
+ chain: client.chain ?? null,
19
+ to: `0x${deployment.signerAddress}`,
20
+ value: BigInt(deployment.gasLimit) * BigInt(deployment.gasPrice),
21
+ });
22
+ const gasReceipt = await waitForTransactionReceipt(client, { hash: gasTx });
23
+ if (gasReceipt.status !== "success") {
24
+ console.error("failed to send gas to deployer signer", gasReceipt);
25
+ throw new Error("failed to send gas to deployer signer");
41
26
  }
42
27
 
43
- // Deploy the deployer
44
- debug("deploying CREATE2 deployer at", deployer);
45
- const deployTx = await sendRawTransaction(client, { serializedTransaction: `0x${deployment.transaction}` }).catch(
46
- (error) => {
47
- // Do a regular contract create if the presigned transaction doesn't work due to replay protection
48
- if (String(error).includes("only replay-protected (EIP-155) transactions allowed over RPC")) {
49
- console.warn(
50
- // eslint-disable-next-line max-len
51
- `\n ⚠️ Your chain or RPC does not allow for non EIP-155 signed transactions, so your deploys will not be determinstic and contract addresses may change between deploys.\n\n We recommend running your chain's node with \`--rpc.allow-unprotected-txs\` to enable determinstic deployments.\n`,
52
- );
53
- debug("deploying CREATE2 deployer");
54
- return sendTransaction(client, {
55
- chain: client.chain ?? null,
56
- data: deployerBytecode,
57
- });
58
- }
59
- throw error;
60
- },
61
- );
62
-
28
+ // deploy the deployer
29
+ debug("deploying create2 deployer at", deployer);
30
+ const deployTx = await sendRawTransaction(client, { serializedTransaction: `0x${deployment.transaction}` });
63
31
  const deployReceipt = await waitForTransactionReceipt(client, { hash: deployTx });
64
- if (!deployReceipt.contractAddress) {
65
- throw new Error("Deploy receipt did not have contract address, was the deployer not deployed?");
66
- }
67
-
68
32
  if (deployReceipt.contractAddress !== deployer) {
69
- console.warn(
70
- `\n ⚠️ CREATE2 deployer created at ${deployReceipt.contractAddress} does not match the CREATE2 determinstic deployer we expected (${deployer})`,
71
- );
33
+ console.error("unexpected contract address for deployer", deployReceipt);
34
+ throw new Error("unexpected contract address for deployer");
72
35
  }
73
-
74
- return deployReceipt.contractAddress;
75
36
  }