@latticexyz/cli 2.0.0-transaction-context-324984c5 → 2.0.1-main-4a6b4598
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 → chunk-QXUPZVZL.js} +2 -2
- package/dist/chunk-QXUPZVZL.js.map +1 -0
- package/dist/commands-WWEJJZV4.js +36 -0
- package/dist/commands-WWEJJZV4.js.map +1 -0
- package/dist/errors-MZURIB7V.js +2 -0
- package/dist/mud.js +1 -1
- package/dist/mud.js.map +1 -1
- package/package.json +16 -15
- package/src/build.ts +9 -5
- package/src/commands/build.ts +2 -3
- package/src/commands/deploy.ts +1 -1
- package/src/commands/dev-contracts.ts +6 -5
- package/src/commands/set-version.ts +1 -1
- package/src/commands/tablegen.ts +2 -2
- package/src/commands/trace.ts +7 -5
- package/src/commands/worldgen.ts +7 -6
- package/src/deploy/common.ts +54 -8
- package/src/deploy/configToTables.ts +8 -6
- package/src/deploy/create2/README.md +4 -0
- package/src/deploy/create2/deployment.json +2 -1
- package/src/deploy/createPrepareDeploy.ts +28 -0
- package/src/deploy/deploy.ts +33 -16
- package/src/deploy/deployWorld.ts +4 -3
- package/src/deploy/ensureContract.ts +12 -7
- package/src/deploy/ensureContractsDeployed.ts +9 -1
- package/src/deploy/ensureDeployer.ts +61 -22
- package/src/deploy/ensureFunctions.ts +5 -5
- package/src/deploy/ensureModules.ts +16 -10
- package/src/deploy/ensureNamespaceOwner.ts +4 -4
- package/src/deploy/ensureSystems.ts +108 -83
- package/src/deploy/ensureTables.ts +8 -9
- package/src/deploy/ensureWorldFactory.ts +79 -92
- package/src/deploy/findLibraries.ts +36 -0
- package/src/deploy/getFunctions.ts +5 -5
- package/src/deploy/getResourceAccess.ts +3 -3
- package/src/deploy/getSystems.ts +6 -7
- package/src/deploy/getTableValue.ts +1 -1
- package/src/deploy/getTables.ts +2 -2
- package/src/deploy/logsToWorldDeploy.ts +4 -4
- package/src/deploy/orderByDependencies.ts +12 -0
- package/src/deploy/resolveConfig.ts +55 -58
- package/src/mud.ts +1 -1
- package/src/mudPackages.ts +1 -1
- package/src/runDeploy.ts +28 -10
- package/src/utils/{modules/constants.ts → defaultModuleContracts.ts} +4 -0
- package/src/utils/errors.ts +1 -1
- package/src/utils/findPlaceholders.ts +27 -0
- package/src/utils/{utils/getContractData.ts → getContractData.ts} +11 -5
- package/src/utils/{utils/postDeploy.ts → postDeploy.ts} +2 -2
- package/src/utils/printMUD.ts +1 -1
- package/dist/chunk-22IIKR4S.js.map +0 -1
- package/dist/commands-3JV3U43E.js +0 -27
- package/dist/commands-3JV3U43E.js.map +0 -1
- package/dist/errors-XGN6V2Y3.js +0 -2
- package/src/deploy/resourceLabel.ts +0 -3
- /package/dist/{errors-XGN6V2Y3.js.map → errors-MZURIB7V.js.map} +0 -0
package/src/commands/build.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import type { CommandModule } from "yargs";
|
2
2
|
import { loadConfig } from "@latticexyz/config/node";
|
3
|
-
import {
|
4
|
-
import { WorldConfig } from "@latticexyz/world";
|
3
|
+
import { World as WorldConfig } from "@latticexyz/world";
|
5
4
|
|
6
5
|
import { getSrcDirectory } from "@latticexyz/common/foundry";
|
7
6
|
import { build } from "../build";
|
@@ -24,7 +23,7 @@ const commandModule: CommandModule<Options, Options> = {
|
|
24
23
|
},
|
25
24
|
|
26
25
|
async handler({ configPath, profile }) {
|
27
|
-
const config = (await loadConfig(configPath)) as
|
26
|
+
const config = (await loadConfig(configPath)) as WorldConfig;
|
28
27
|
const srcDir = await getSrcDirectory();
|
29
28
|
|
30
29
|
await build({ config, srcDir, foundryProfile: profile });
|
package/src/commands/deploy.ts
CHANGED
@@ -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) {
|
19
19
|
logError(error);
|
20
20
|
process.exit(1);
|
21
21
|
}
|
@@ -3,9 +3,8 @@ import { anvil, getScriptDirectory, getSrcDirectory } from "@latticexyz/common/f
|
|
3
3
|
import chalk from "chalk";
|
4
4
|
import chokidar from "chokidar";
|
5
5
|
import { loadConfig, resolveConfigPath } from "@latticexyz/config/node";
|
6
|
-
import { StoreConfig } from "@latticexyz/store";
|
7
6
|
import path from "path";
|
8
|
-
import { WorldConfig } from "@latticexyz/world";
|
7
|
+
import { World as WorldConfig } from "@latticexyz/world";
|
9
8
|
import { homedir } from "os";
|
10
9
|
import { rmSync } from "fs";
|
11
10
|
import { deployOptions, runDeploy } from "../runDeploy";
|
@@ -34,7 +33,7 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
|
|
34
33
|
const configPath = opts.configPath ?? (await resolveConfigPath(opts.configPath));
|
35
34
|
const srcDir = await getSrcDirectory();
|
36
35
|
const scriptDir = await getScriptDirectory();
|
37
|
-
const initialConfig = (await loadConfig(configPath)) as
|
36
|
+
const initialConfig = (await loadConfig(configPath)) as WorldConfig;
|
38
37
|
|
39
38
|
// Start an anvil instance in the background if no RPC url is provided
|
40
39
|
if (!opts.rpc) {
|
@@ -61,7 +60,7 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
|
|
61
60
|
}
|
62
61
|
if (updatePath.includes(srcDir) || updatePath.includes(scriptDir)) {
|
63
62
|
// Ignore changes to codegen files to avoid an infinite loop
|
64
|
-
if (!updatePath.includes(initialConfig.
|
63
|
+
if (!updatePath.includes(initialConfig.codegen.outputDirectory)) {
|
65
64
|
console.log(chalk.blue("Contracts changed, queuing deploy…"));
|
66
65
|
lastChange$.next(Date.now());
|
67
66
|
}
|
@@ -83,10 +82,12 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
|
|
83
82
|
...opts,
|
84
83
|
configPath,
|
85
84
|
rpc,
|
85
|
+
rpcBatch: false,
|
86
86
|
skipBuild: false,
|
87
87
|
printConfig: false,
|
88
88
|
profile: undefined,
|
89
89
|
saveDeployment: true,
|
90
|
+
deployerAddress: undefined,
|
90
91
|
worldAddress,
|
91
92
|
srcDir,
|
92
93
|
salt: "0x",
|
@@ -105,7 +106,7 @@ const commandModule: CommandModule<typeof devOptions, InferredOptionTypes<typeof
|
|
105
106
|
console.log(chalk.gray("\nWaiting for file changes…\n"));
|
106
107
|
}
|
107
108
|
}),
|
108
|
-
filter(isDefined)
|
109
|
+
filter(isDefined),
|
109
110
|
);
|
110
111
|
|
111
112
|
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) {
|
package/src/commands/tablegen.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import path from "path";
|
2
2
|
import type { CommandModule } from "yargs";
|
3
3
|
import { loadConfig } from "@latticexyz/config/node";
|
4
|
-
import { StoreConfig } from "@latticexyz/store";
|
4
|
+
import { Store as StoreConfig } from "@latticexyz/store";
|
5
5
|
import { tablegen } from "@latticexyz/store/codegen";
|
6
6
|
import { getRemappings, getSrcDirectory } from "@latticexyz/common/foundry";
|
7
7
|
|
@@ -25,7 +25,7 @@ const commandModule: CommandModule<Options, Options> = {
|
|
25
25
|
const srcDir = await getSrcDirectory();
|
26
26
|
const remappings = await getRemappings();
|
27
27
|
|
28
|
-
await tablegen(config, path.join(srcDir, config.
|
28
|
+
await tablegen(config, path.join(srcDir, config.codegen.outputDirectory), remappings);
|
29
29
|
|
30
30
|
process.exit(0);
|
31
31
|
},
|
package/src/commands/trace.ts
CHANGED
@@ -5,20 +5,21 @@ import { ethers } from "ethers";
|
|
5
5
|
import { loadConfig } from "@latticexyz/config/node";
|
6
6
|
import { MUDError } from "@latticexyz/common/errors";
|
7
7
|
import { cast, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
|
8
|
-
import {
|
9
|
-
import { resolveWorldConfig, WorldConfig } from "@latticexyz/world";
|
8
|
+
import { resolveWorldConfig } from "@latticexyz/world/internal";
|
10
9
|
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json" assert { type: "json" };
|
11
10
|
import worldConfig from "@latticexyz/world/mud.config";
|
12
11
|
import { resourceToHex } from "@latticexyz/common";
|
13
12
|
import { getExistingContracts } from "../utils/getExistingContracts";
|
14
13
|
import { createClient, http } from "viem";
|
15
14
|
import { getChainId } from "viem/actions";
|
15
|
+
import { World as WorldConfig } from "@latticexyz/world";
|
16
|
+
import { worldToV1 } from "@latticexyz/world/config/v2";
|
16
17
|
|
17
18
|
// TODO account for multiple namespaces (https://github.com/latticexyz/mud/issues/994)
|
18
19
|
const systemsTableId = resourceToHex({
|
19
20
|
type: "system",
|
20
21
|
namespace: worldConfig.namespace,
|
21
|
-
name: worldConfig.tables.
|
22
|
+
name: worldConfig.tables.world__Systems.name,
|
22
23
|
});
|
23
24
|
|
24
25
|
type Options = {
|
@@ -59,11 +60,12 @@ const commandModule: CommandModule<Options, Options> = {
|
|
59
60
|
const existingContracts = getExistingContracts(srcDir);
|
60
61
|
|
61
62
|
// Load the config
|
62
|
-
const
|
63
|
+
const configV2 = (await loadConfig(configPath)) as WorldConfig;
|
64
|
+
const mudConfig = worldToV1(configV2);
|
63
65
|
|
64
66
|
const resolvedConfig = resolveWorldConfig(
|
65
67
|
mudConfig,
|
66
|
-
existingContracts.map(({ basename }) => basename)
|
68
|
+
existingContracts.map(({ basename }) => basename),
|
67
69
|
);
|
68
70
|
|
69
71
|
// Get worldAddress either from args or from worldsFile
|
package/src/commands/worldgen.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import type { CommandModule } from "yargs";
|
2
2
|
import { loadConfig } from "@latticexyz/config/node";
|
3
|
-
import {
|
4
|
-
import { WorldConfig } from "@latticexyz/world";
|
3
|
+
import { World as WorldConfig } from "@latticexyz/world";
|
5
4
|
import { worldgen } from "@latticexyz/world/node";
|
6
5
|
import { getSrcDirectory } from "@latticexyz/common/foundry";
|
7
6
|
import path from "path";
|
@@ -12,7 +11,7 @@ type Options = {
|
|
12
11
|
configPath?: string;
|
13
12
|
clean?: boolean;
|
14
13
|
srcDir?: string;
|
15
|
-
config?:
|
14
|
+
config?: WorldConfig;
|
16
15
|
};
|
17
16
|
|
18
17
|
const commandModule: CommandModule<Options, Options> = {
|
@@ -43,12 +42,14 @@ export async function worldgenHandler(args: Options) {
|
|
43
42
|
const existingContracts = getExistingContracts(srcDir);
|
44
43
|
|
45
44
|
// Load the config
|
46
|
-
const mudConfig = args.config ?? ((await loadConfig(args.configPath)) as
|
45
|
+
const mudConfig = args.config ?? ((await loadConfig(args.configPath)) as WorldConfig);
|
47
46
|
|
48
|
-
const outputBaseDirectory = path.join(srcDir, mudConfig.
|
47
|
+
const outputBaseDirectory = path.join(srcDir, mudConfig.codegen.outputDirectory);
|
49
48
|
|
50
49
|
// clear the worldgen directory
|
51
|
-
if (args.clean)
|
50
|
+
if (args.clean) {
|
51
|
+
rmSync(path.join(outputBaseDirectory, mudConfig.codegen.worldgenDirectory), { recursive: true, force: true });
|
52
|
+
}
|
52
53
|
|
53
54
|
// generate new interfaces
|
54
55
|
await worldgen(mudConfig, existingContracts, outputBaseDirectory);
|
package/src/deploy/common.ts
CHANGED
@@ -4,8 +4,12 @@ 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";
|
7
|
-
import {
|
8
|
-
import {
|
7
|
+
import { helloStoreEvent } from "@latticexyz/store";
|
8
|
+
import { StoreConfig } from "@latticexyz/store/internal";
|
9
|
+
import { helloWorldEvent } from "@latticexyz/world";
|
10
|
+
import { WorldConfig } from "@latticexyz/world/internal";
|
11
|
+
import { storeToV1 } from "@latticexyz/store/config/v2";
|
12
|
+
import { worldToV1 } from "@latticexyz/world/config/v2";
|
9
13
|
|
10
14
|
export const salt = padHex("0x", { size: 32 });
|
11
15
|
|
@@ -13,17 +17,18 @@ export const salt = padHex("0x", { size: 32 });
|
|
13
17
|
export const contractSizeLimit = parseInt("6000", 16);
|
14
18
|
|
15
19
|
// TODO: add `as const` to mud config so these get more strongly typed (blocked by current config parsing not using readonly)
|
16
|
-
export const storeTables = configToTables(storeConfig);
|
17
|
-
export const worldTables = configToTables(worldConfig);
|
20
|
+
export const storeTables = configToTables(storeToV1(storeConfig));
|
21
|
+
export const worldTables = configToTables(worldToV1(worldConfig));
|
18
22
|
|
19
23
|
export const worldDeployEvents = [helloStoreEvent, helloWorldEvent] as const;
|
20
24
|
|
21
25
|
export const worldAbi = [...IBaseWorldAbi, ...IModuleAbi] as const;
|
22
26
|
|
23
27
|
// Ideally, this should be an append-only list. Before adding more versions here, be sure to add backwards-compatible support for old Store/World versions.
|
24
|
-
export const supportedStoreVersions = ["
|
25
|
-
export const supportedWorldVersions = ["
|
28
|
+
export const supportedStoreVersions = ["2.0.0"];
|
29
|
+
export const supportedWorldVersions = ["2.0.0"];
|
26
30
|
|
31
|
+
// TODO: extend this to include factory+deployer address? so we can reuse the deployer for a world?
|
27
32
|
export type WorldDeploy = {
|
28
33
|
readonly address: Address;
|
29
34
|
readonly worldVersion: string;
|
@@ -46,22 +51,62 @@ export type WorldFunction = {
|
|
46
51
|
readonly systemFunctionSelector: Hex;
|
47
52
|
};
|
48
53
|
|
54
|
+
export type LibraryPlaceholder = {
|
55
|
+
/**
|
56
|
+
* Path to library source file, e.g. `src/libraries/SomeLib.sol`
|
57
|
+
*/
|
58
|
+
path: string;
|
59
|
+
/**
|
60
|
+
* Library name, e.g. `SomeLib`
|
61
|
+
*/
|
62
|
+
name: string;
|
63
|
+
/**
|
64
|
+
* Byte offset of placeholder in bytecode
|
65
|
+
*/
|
66
|
+
start: number;
|
67
|
+
/**
|
68
|
+
* Size of placeholder to replace in bytes
|
69
|
+
*/
|
70
|
+
length: number;
|
71
|
+
};
|
72
|
+
|
49
73
|
export type DeterministicContract = {
|
50
|
-
readonly
|
51
|
-
|
74
|
+
readonly prepareDeploy: (
|
75
|
+
deployer: Address,
|
76
|
+
libraries: readonly Library[],
|
77
|
+
) => {
|
78
|
+
readonly address: Address;
|
79
|
+
readonly bytecode: Hex;
|
80
|
+
};
|
52
81
|
readonly deployedBytecodeSize: number;
|
53
82
|
readonly abi: Abi;
|
54
83
|
};
|
55
84
|
|
85
|
+
export type Library = DeterministicContract & {
|
86
|
+
/**
|
87
|
+
* Path to library source file, e.g. `src/libraries/SomeLib.sol`
|
88
|
+
*/
|
89
|
+
path: string;
|
90
|
+
/**
|
91
|
+
* Library name, e.g. `SomeLib`
|
92
|
+
*/
|
93
|
+
name: string;
|
94
|
+
};
|
95
|
+
|
56
96
|
export type System = DeterministicContract & {
|
57
97
|
readonly namespace: string;
|
58
98
|
readonly name: string;
|
59
99
|
readonly systemId: Hex;
|
60
100
|
readonly allowAll: boolean;
|
61
101
|
readonly allowedAddresses: readonly Hex[];
|
102
|
+
readonly allowedSystemIds: readonly Hex[];
|
62
103
|
readonly functions: readonly WorldFunction[];
|
63
104
|
};
|
64
105
|
|
106
|
+
export type DeployedSystem = Omit<System, "abi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds"> & {
|
107
|
+
address: Address;
|
108
|
+
};
|
109
|
+
|
65
110
|
export type Module = DeterministicContract & {
|
66
111
|
readonly name: string;
|
67
112
|
readonly installAsRoot: boolean;
|
@@ -73,4 +118,5 @@ export type Config<config extends ConfigInput> = {
|
|
73
118
|
readonly tables: Tables<config>;
|
74
119
|
readonly systems: readonly System[];
|
75
120
|
readonly modules: readonly Module[];
|
121
|
+
readonly libraries: readonly Library[];
|
76
122
|
};
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { resourceToHex } from "@latticexyz/common";
|
2
|
-
import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser";
|
3
|
-
import { SchemaAbiType, StaticAbiType } from "@latticexyz/schema-type";
|
4
|
-
import { StoreConfig, resolveUserTypes } from "@latticexyz/store";
|
2
|
+
import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser/internal";
|
3
|
+
import { SchemaAbiType, StaticAbiType } from "@latticexyz/schema-type/internal";
|
4
|
+
import { StoreConfig, resolveUserTypes } from "@latticexyz/store/internal";
|
5
5
|
import { Hex } from "viem";
|
6
6
|
|
7
7
|
// TODO: we shouldn't need this file once our config parsing returns nicely formed tables
|
@@ -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,9 +60,11 @@ 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
|
63
64
|
keySchema: resolveUserTypes(table.keySchema, userTypes) as any,
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
64
66
|
valueSchema: resolveUserTypes(table.valueSchema, userTypes) as any,
|
65
67
|
} satisfies Table<config, config["tables"][keyof config["tables"]]>,
|
66
|
-
])
|
68
|
+
]),
|
67
69
|
) as Tables<config>;
|
68
70
|
}
|
@@ -6,4 +6,8 @@ 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
|
9
13
|
```
|
@@ -3,5 +3,6 @@
|
|
3
3
|
"gasLimit": 100000,
|
4
4
|
"signerAddress": "3fab184622dc19b6109349b94811493bf2a45362",
|
5
5
|
"transaction": "f8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf31ba02222222222222222222222222222222222222222222222222222222222222222a02222222222222222222222222222222222222222222222222222222222222222",
|
6
|
-
"address": "4e59b44847b379578588920ca78fbf26c0b4956c"
|
6
|
+
"address": "4e59b44847b379578588920ca78fbf26c0b4956c",
|
7
|
+
"bytecode": "604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"
|
7
8
|
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { DeterministicContract, Library, LibraryPlaceholder, salt } from "./common";
|
2
|
+
import { spliceHex } from "@latticexyz/common";
|
3
|
+
import { Hex, getCreate2Address, Address } from "viem";
|
4
|
+
|
5
|
+
export function createPrepareDeploy(
|
6
|
+
bytecodeWithPlaceholders: Hex,
|
7
|
+
placeholders: readonly LibraryPlaceholder[],
|
8
|
+
): DeterministicContract["prepareDeploy"] {
|
9
|
+
return function prepareDeploy(deployer: Address, libraries: readonly Library[]) {
|
10
|
+
let bytecode = bytecodeWithPlaceholders;
|
11
|
+
for (const placeholder of placeholders) {
|
12
|
+
const library = libraries.find((lib) => lib.path === placeholder.path && lib.name === placeholder.name);
|
13
|
+
if (!library) {
|
14
|
+
throw new Error(`Could not find library for bytecode placeholder ${placeholder.path}:${placeholder.name}`);
|
15
|
+
}
|
16
|
+
bytecode = spliceHex(
|
17
|
+
bytecode,
|
18
|
+
placeholder.start,
|
19
|
+
placeholder.length,
|
20
|
+
library.prepareDeploy(deployer, libraries).address,
|
21
|
+
);
|
22
|
+
}
|
23
|
+
return {
|
24
|
+
bytecode,
|
25
|
+
address: getCreate2Address({ from: deployer, bytecode, salt }),
|
26
|
+
};
|
27
|
+
};
|
28
|
+
}
|
package/src/deploy/deploy.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Account, Address, Chain, Client, Hex, Transport
|
1
|
+
import { Account, Address, Chain, Client, Hex, Transport } from "viem";
|
2
2
|
import { ensureDeployer } from "./ensureDeployer";
|
3
3
|
import { deployWorld } from "./deployWorld";
|
4
4
|
import { ensureTables } from "./ensureTables";
|
@@ -11,17 +11,23 @@ import { ensureModules } from "./ensureModules";
|
|
11
11
|
import { Table } from "./configToTables";
|
12
12
|
import { ensureNamespaceOwner } from "./ensureNamespaceOwner";
|
13
13
|
import { debug } from "./debug";
|
14
|
-
import {
|
15
|
-
import { uniqueBy } from "@latticexyz/common/utils";
|
14
|
+
import { resourceToLabel } from "@latticexyz/common";
|
16
15
|
import { ensureContractsDeployed } from "./ensureContractsDeployed";
|
17
|
-
import { worldFactoryContracts } from "./ensureWorldFactory";
|
18
16
|
import { randomBytes } from "crypto";
|
17
|
+
import { ensureWorldFactory } from "./ensureWorldFactory";
|
19
18
|
|
20
19
|
type DeployOptions<configInput extends ConfigInput> = {
|
21
20
|
client: Client<Transport, Chain | undefined, Account>;
|
22
21
|
config: Config<configInput>;
|
23
22
|
salt?: Hex;
|
24
23
|
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;
|
25
31
|
};
|
26
32
|
|
27
33
|
/**
|
@@ -35,24 +41,31 @@ export async function deploy<configInput extends ConfigInput>({
|
|
35
41
|
config,
|
36
42
|
salt,
|
37
43
|
worldAddress: existingWorldAddress,
|
44
|
+
deployerAddress: initialDeployerAddress,
|
38
45
|
}: DeployOptions<configInput>): Promise<WorldDeploy> {
|
39
46
|
const tables = Object.values(config.tables) as Table[];
|
40
|
-
const systems = Object.values(config.systems);
|
41
47
|
|
42
|
-
await ensureDeployer(client);
|
48
|
+
const deployerAddress = initialDeployerAddress ?? (await ensureDeployer(client));
|
49
|
+
|
50
|
+
await ensureWorldFactory(client, deployerAddress);
|
43
51
|
|
44
52
|
// deploy all dependent contracts, because system registration, module install, etc. all expect these contracts to be callable.
|
45
53
|
await ensureContractsDeployed({
|
46
54
|
client,
|
55
|
+
deployerAddress,
|
47
56
|
contracts: [
|
48
|
-
...
|
49
|
-
|
50
|
-
|
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,
|
51
64
|
deployedBytecodeSize: system.deployedBytecodeSize,
|
52
|
-
label: `${
|
65
|
+
label: `${resourceToLabel(system)} system`,
|
53
66
|
})),
|
54
|
-
...
|
55
|
-
bytecode: mod.bytecode,
|
67
|
+
...config.modules.map((mod) => ({
|
68
|
+
bytecode: mod.prepareDeploy(deployerAddress, config.libraries).bytecode,
|
56
69
|
deployedBytecodeSize: mod.deployedBytecodeSize,
|
57
70
|
label: `${mod.name} module`,
|
58
71
|
})),
|
@@ -61,7 +74,7 @@ export async function deploy<configInput extends ConfigInput>({
|
|
61
74
|
|
62
75
|
const worldDeploy = existingWorldAddress
|
63
76
|
? await getWorldDeploy(client, existingWorldAddress)
|
64
|
-
: await deployWorld(client,
|
77
|
+
: await deployWorld(client, deployerAddress, salt ?? `0x${randomBytes(32).toString("hex")}`);
|
65
78
|
|
66
79
|
if (!supportedStoreVersions.includes(worldDeploy.storeVersion)) {
|
67
80
|
throw new Error(`Unsupported Store version: ${worldDeploy.storeVersion}`);
|
@@ -73,7 +86,7 @@ export async function deploy<configInput extends ConfigInput>({
|
|
73
86
|
const namespaceTxs = await ensureNamespaceOwner({
|
74
87
|
client,
|
75
88
|
worldDeploy,
|
76
|
-
resourceIds: [...tables.map((table) => table.tableId), ...systems.map((system) => system.systemId)],
|
89
|
+
resourceIds: [...tables.map((table) => table.tableId), ...config.systems.map((system) => system.systemId)],
|
77
90
|
});
|
78
91
|
|
79
92
|
debug("waiting for all namespace registration transactions to confirm");
|
@@ -88,16 +101,20 @@ export async function deploy<configInput extends ConfigInput>({
|
|
88
101
|
});
|
89
102
|
const systemTxs = await ensureSystems({
|
90
103
|
client,
|
104
|
+
deployerAddress,
|
105
|
+
libraries: config.libraries,
|
91
106
|
worldDeploy,
|
92
|
-
systems,
|
107
|
+
systems: config.systems,
|
93
108
|
});
|
94
109
|
const functionTxs = await ensureFunctions({
|
95
110
|
client,
|
96
111
|
worldDeploy,
|
97
|
-
functions: systems.flatMap((system) => system.functions),
|
112
|
+
functions: config.systems.flatMap((system) => system.functions),
|
98
113
|
});
|
99
114
|
const moduleTxs = await ensureModules({
|
100
115
|
client,
|
116
|
+
deployerAddress,
|
117
|
+
libraries: config.libraries,
|
101
118
|
worldDeploy,
|
102
119
|
modules: config.modules,
|
103
120
|
});
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Account, Chain, Client, Hex, Log, Transport } from "viem";
|
2
2
|
import { waitForTransactionReceipt } from "viem/actions";
|
3
|
-
import { ensureWorldFactory
|
3
|
+
import { ensureWorldFactory } 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";
|
@@ -9,9 +9,10 @@ import { WorldDeploy } from "./common";
|
|
9
9
|
|
10
10
|
export async function deployWorld(
|
11
11
|
client: Client<Transport, Chain | undefined, Account>,
|
12
|
-
|
12
|
+
deployerAddress: Hex,
|
13
|
+
salt: Hex,
|
13
14
|
): Promise<WorldDeploy> {
|
14
|
-
await ensureWorldFactory(client);
|
15
|
+
const worldFactory = await ensureWorldFactory(client, deployerAddress);
|
15
16
|
|
16
17
|
debug("deploying world");
|
17
18
|
const tx = await writeContract(client, {
|
@@ -1,6 +1,5 @@
|
|
1
|
-
import { Client, Transport, Chain, Account, concatHex, getCreate2Address, Hex
|
1
|
+
import { Client, Transport, Chain, Account, concatHex, getCreate2Address, Hex } from "viem";
|
2
2
|
import { getBytecode } from "viem/actions";
|
3
|
-
import { deployer } from "./ensureDeployer";
|
4
3
|
import { contractSizeLimit, salt } from "./common";
|
5
4
|
import { sendTransaction } from "@latticexyz/common";
|
6
5
|
import { debug } from "./debug";
|
@@ -15,13 +14,19 @@ export type Contract = {
|
|
15
14
|
|
16
15
|
export async function ensureContract({
|
17
16
|
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;
|
23
24
|
} & Contract): Promise<readonly Hex[]> {
|
24
|
-
|
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 });
|
25
30
|
|
26
31
|
const contractCode = await getBytecode(client, { address, blockTag: "pending" });
|
27
32
|
if (contractCode) {
|
@@ -31,11 +36,11 @@ export async function ensureContract({
|
|
31
36
|
|
32
37
|
if (deployedBytecodeSize > contractSizeLimit) {
|
33
38
|
console.warn(
|
34
|
-
`\nBytecode for ${label} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n
|
39
|
+
`\nBytecode for ${label} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
|
35
40
|
);
|
36
41
|
} else if (deployedBytecodeSize > contractSizeLimit * 0.95) {
|
37
42
|
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
|
43
|
+
`\nBytecode for ${label} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`,
|
39
44
|
);
|
40
45
|
}
|
41
46
|
|
@@ -45,7 +50,7 @@ export async function ensureContract({
|
|
45
50
|
() =>
|
46
51
|
sendTransaction(client, {
|
47
52
|
chain: client.chain ?? null,
|
48
|
-
to:
|
53
|
+
to: deployerAddress,
|
49
54
|
data: concatHex([salt, bytecode]),
|
50
55
|
}),
|
51
56
|
{
|
@@ -55,7 +60,7 @@ export async function ensureContract({
|
|
55
60
|
debug(`failed to deploy ${label}, retrying in ${delay}ms...`);
|
56
61
|
await wait(delay);
|
57
62
|
},
|
58
|
-
}
|
63
|
+
},
|
59
64
|
),
|
60
65
|
];
|
61
66
|
}
|
@@ -2,15 +2,23 @@ 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";
|
5
6
|
|
6
7
|
export async function ensureContractsDeployed({
|
7
8
|
client,
|
9
|
+
deployerAddress,
|
8
10
|
contracts,
|
9
11
|
}: {
|
10
12
|
readonly client: Client<Transport, Chain | undefined, Account>;
|
13
|
+
readonly deployerAddress: Hex;
|
11
14
|
readonly contracts: readonly Contract[];
|
12
15
|
}): Promise<readonly Hex[]> {
|
13
|
-
|
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();
|
14
22
|
|
15
23
|
if (txs.length) {
|
16
24
|
debug("waiting for contracts");
|