@latticexyz/cli 2.0.7-account-kit-49e54f3e → 2.0.7-account-kit-46c44c7e
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/commands-LUTSZ5VT.js +38 -0
- package/dist/commands-LUTSZ5VT.js.map +1 -0
- package/dist/mud.js +1 -1
- package/package.json +13 -12
- package/src/commands/build.ts +1 -1
- package/src/commands/dev-contracts.ts +1 -0
- package/src/commands/index.ts +2 -0
- package/src/commands/tablegen.ts +1 -1
- package/src/commands/trace.ts +1 -1
- package/src/commands/verify.ts +111 -0
- package/src/commands/worldgen.ts +1 -1
- package/src/deploy/deploy.ts +4 -2
- package/src/deploy/deployWorld.ts +2 -1
- package/src/deploy/ensureDeployer.ts +6 -12
- package/src/deploy/ensureWorldFactory.ts +19 -94
- package/src/deploy/getDeployer.ts +20 -0
- package/src/deploy/getResourceIds.ts +23 -9
- package/src/deploy/getWorldContracts.ts +79 -0
- package/src/deploy/getWorldFactoryContracts.ts +27 -0
- package/src/deploy/getWorldProxyFactoryContracts.ts +27 -0
- package/src/runDeploy.ts +4 -2
- package/src/utils/postDeploy.ts +34 -10
- package/src/verify/verifyContract.ts +24 -0
- package/src/verify.ts +166 -0
- package/dist/commands-NEAVTLVT.js +0 -36
- package/dist/commands-NEAVTLVT.js.map +0 -1
package/src/commands/index.ts
CHANGED
@@ -14,6 +14,7 @@ import setVersion from "./set-version";
|
|
14
14
|
import test from "./test";
|
15
15
|
import trace from "./trace";
|
16
16
|
import devContracts from "./dev-contracts";
|
17
|
+
import verify from "./verify";
|
17
18
|
|
18
19
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each command has different options
|
19
20
|
export const commands: CommandModule<any, any>[] = [
|
@@ -30,4 +31,5 @@ export const commands: CommandModule<any, any>[] = [
|
|
30
31
|
trace,
|
31
32
|
devContracts,
|
32
33
|
abiTs,
|
34
|
+
verify,
|
33
35
|
];
|
package/src/commands/tablegen.ts
CHANGED
@@ -16,7 +16,7 @@ const commandModule: CommandModule<Options, Options> = {
|
|
16
16
|
|
17
17
|
builder(yargs) {
|
18
18
|
return yargs.options({
|
19
|
-
configPath: { type: "string", desc: "Path to the config file" },
|
19
|
+
configPath: { type: "string", desc: "Path to the MUD config file" },
|
20
20
|
});
|
21
21
|
},
|
22
22
|
|
package/src/commands/trace.ts
CHANGED
@@ -43,7 +43,7 @@ const commandModule: CommandModule<Options, Options> = {
|
|
43
43
|
type: "string",
|
44
44
|
description: "World contract address. Defaults to the value from worlds.json, based on rpc's chainId",
|
45
45
|
},
|
46
|
-
configPath: { type: "string", description: "Path to the config file" },
|
46
|
+
configPath: { type: "string", description: "Path to the MUD config file" },
|
47
47
|
profile: { type: "string", description: "The foundry profile to use" },
|
48
48
|
srcDir: { type: "string", description: "Source directory. Defaults to foundry src directory." },
|
49
49
|
rpc: { type: "string", description: "json rpc endpoint. Defaults to foundry's configured eth_rpc_url" },
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import type { CommandModule, InferredOptionTypes } from "yargs";
|
2
|
+
import { verify } from "../verify";
|
3
|
+
import { loadConfig } from "@latticexyz/config/node";
|
4
|
+
import { World as WorldConfig } from "@latticexyz/world";
|
5
|
+
import { resolveWorldConfig } from "@latticexyz/world/internal";
|
6
|
+
import { worldToV1 } from "@latticexyz/world/config/v2";
|
7
|
+
import { getOutDirectory, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
|
8
|
+
import { getExistingContracts } from "../utils/getExistingContracts";
|
9
|
+
import { getContractData } from "../utils/getContractData";
|
10
|
+
import { defaultModuleContracts } from "../utils/defaultModuleContracts";
|
11
|
+
import { Hex, createWalletClient, http } from "viem";
|
12
|
+
import chalk from "chalk";
|
13
|
+
|
14
|
+
const verifyOptions = {
|
15
|
+
deployerAddress: {
|
16
|
+
type: "string",
|
17
|
+
desc: "Deploy using an existing deterministic deployer (https://github.com/Arachnid/deterministic-deployment-proxy)",
|
18
|
+
},
|
19
|
+
worldAddress: { type: "string", required: true, desc: "Verify an existing World at the given address" },
|
20
|
+
configPath: { type: "string", desc: "Path to the MUD config file" },
|
21
|
+
profile: { type: "string", desc: "The foundry profile to use" },
|
22
|
+
rpc: { type: "string", desc: "The RPC URL to use. Defaults to the RPC url from the local foundry.toml" },
|
23
|
+
rpcBatch: {
|
24
|
+
type: "boolean",
|
25
|
+
desc: "Enable batch processing of RPC requests in viem client (defaults to batch size of 100 and wait of 1s)",
|
26
|
+
},
|
27
|
+
srcDir: { type: "string", desc: "Source directory. Defaults to foundry src directory." },
|
28
|
+
verifier: { type: "string", desc: "The verifier to use" },
|
29
|
+
verifierUrl: {
|
30
|
+
type: "string",
|
31
|
+
desc: "The verification provider.",
|
32
|
+
},
|
33
|
+
} as const;
|
34
|
+
|
35
|
+
type Options = InferredOptionTypes<typeof verifyOptions>;
|
36
|
+
|
37
|
+
const commandModule: CommandModule<Options, Options> = {
|
38
|
+
command: "verify",
|
39
|
+
|
40
|
+
describe: "Verify contracts",
|
41
|
+
|
42
|
+
builder(yargs) {
|
43
|
+
return yargs.options(verifyOptions);
|
44
|
+
},
|
45
|
+
|
46
|
+
async handler(opts) {
|
47
|
+
const profile = opts.profile ?? process.env.FOUNDRY_PROFILE;
|
48
|
+
|
49
|
+
const configV2 = (await loadConfig(opts.configPath)) as WorldConfig;
|
50
|
+
const config = worldToV1(configV2);
|
51
|
+
|
52
|
+
const srcDir = opts.srcDir ?? (await getSrcDirectory(profile));
|
53
|
+
const outDir = await getOutDirectory(profile);
|
54
|
+
|
55
|
+
const rpc = opts.rpc ?? (await getRpcUrl(profile));
|
56
|
+
console.log(
|
57
|
+
chalk.bgBlue(
|
58
|
+
chalk.whiteBright(`\n Verifying MUD contracts${profile ? " with profile " + profile : ""} to RPC ${rpc} \n`),
|
59
|
+
),
|
60
|
+
);
|
61
|
+
|
62
|
+
const client = createWalletClient({
|
63
|
+
transport: http(rpc, {
|
64
|
+
batch: opts.rpcBatch
|
65
|
+
? {
|
66
|
+
batchSize: 100,
|
67
|
+
wait: 1000,
|
68
|
+
}
|
69
|
+
: undefined,
|
70
|
+
}),
|
71
|
+
});
|
72
|
+
|
73
|
+
const contractNames = getExistingContracts(srcDir).map(({ basename }) => basename);
|
74
|
+
const resolvedWorldConfig = resolveWorldConfig(config, contractNames);
|
75
|
+
|
76
|
+
const systems = Object.keys(resolvedWorldConfig.systems).map((name) => {
|
77
|
+
const contractData = getContractData(`${name}.sol`, name, outDir);
|
78
|
+
|
79
|
+
return {
|
80
|
+
name,
|
81
|
+
bytecode: contractData.bytecode,
|
82
|
+
};
|
83
|
+
});
|
84
|
+
|
85
|
+
// Get modules
|
86
|
+
const modules = config.modules.map((mod) => {
|
87
|
+
const contractData =
|
88
|
+
defaultModuleContracts.find((defaultMod) => defaultMod.name === mod.name) ??
|
89
|
+
getContractData(`${mod.name}.sol`, mod.name, outDir);
|
90
|
+
|
91
|
+
return {
|
92
|
+
name: mod.name,
|
93
|
+
bytecode: contractData.bytecode,
|
94
|
+
};
|
95
|
+
});
|
96
|
+
|
97
|
+
await verify({
|
98
|
+
client,
|
99
|
+
rpc,
|
100
|
+
foundryProfile: profile,
|
101
|
+
systems,
|
102
|
+
modules,
|
103
|
+
deployerAddress: opts.deployerAddress as Hex | undefined,
|
104
|
+
worldAddress: opts.worldAddress as Hex,
|
105
|
+
verifier: opts.verifier,
|
106
|
+
verifierUrl: opts.verifierUrl,
|
107
|
+
});
|
108
|
+
},
|
109
|
+
};
|
110
|
+
|
111
|
+
export default commandModule;
|
package/src/commands/worldgen.ts
CHANGED
@@ -21,7 +21,7 @@ const commandModule: CommandModule<Options, Options> = {
|
|
21
21
|
|
22
22
|
builder(yargs) {
|
23
23
|
return yargs.options({
|
24
|
-
configPath: { type: "string", desc: "Path to the config file" },
|
24
|
+
configPath: { type: "string", desc: "Path to the MUD config file" },
|
25
25
|
clean: {
|
26
26
|
type: "boolean",
|
27
27
|
desc: "Clear the worldgen directory before generating new interfaces (defaults to true)",
|
package/src/deploy/deploy.ts
CHANGED
@@ -28,6 +28,7 @@ type DeployOptions<configInput extends ConfigInput> = {
|
|
28
28
|
* not have a deterministic address.
|
29
29
|
*/
|
30
30
|
deployerAddress?: Hex;
|
31
|
+
withWorldProxy?: boolean;
|
31
32
|
};
|
32
33
|
|
33
34
|
/**
|
@@ -42,12 +43,13 @@ export async function deploy<configInput extends ConfigInput>({
|
|
42
43
|
salt,
|
43
44
|
worldAddress: existingWorldAddress,
|
44
45
|
deployerAddress: initialDeployerAddress,
|
46
|
+
withWorldProxy,
|
45
47
|
}: DeployOptions<configInput>): Promise<WorldDeploy> {
|
46
48
|
const tables = Object.values(config.tables) as Table[];
|
47
49
|
|
48
50
|
const deployerAddress = initialDeployerAddress ?? (await ensureDeployer(client));
|
49
51
|
|
50
|
-
await ensureWorldFactory(client, deployerAddress);
|
52
|
+
await ensureWorldFactory(client, deployerAddress, withWorldProxy);
|
51
53
|
|
52
54
|
// deploy all dependent contracts, because system registration, module install, etc. all expect these contracts to be callable.
|
53
55
|
await ensureContractsDeployed({
|
@@ -74,7 +76,7 @@ export async function deploy<configInput extends ConfigInput>({
|
|
74
76
|
|
75
77
|
const worldDeploy = existingWorldAddress
|
76
78
|
? await getWorldDeploy(client, existingWorldAddress)
|
77
|
-
: await deployWorld(client, deployerAddress, salt ?? `0x${randomBytes(32).toString("hex")}
|
79
|
+
: await deployWorld(client, deployerAddress, salt ?? `0x${randomBytes(32).toString("hex")}`, withWorldProxy);
|
78
80
|
|
79
81
|
if (!supportedStoreVersions.includes(worldDeploy.storeVersion)) {
|
80
82
|
throw new Error(`Unsupported Store version: ${worldDeploy.storeVersion}`);
|
@@ -11,8 +11,9 @@ export async function deployWorld(
|
|
11
11
|
client: Client<Transport, Chain | undefined, Account>,
|
12
12
|
deployerAddress: Hex,
|
13
13
|
salt: Hex,
|
14
|
+
withWorldProxy?: boolean,
|
14
15
|
): Promise<WorldDeploy> {
|
15
|
-
const worldFactory = await ensureWorldFactory(client, deployerAddress);
|
16
|
+
const worldFactory = await ensureWorldFactory(client, deployerAddress, withWorldProxy);
|
16
17
|
|
17
18
|
debug("deploying world");
|
18
19
|
const tx = await writeContract(client, {
|
@@ -1,21 +1,15 @@
|
|
1
|
-
import { Account, Address, Chain, Client, Transport
|
2
|
-
import { getBalance,
|
1
|
+
import { Account, Address, Chain, Client, Transport } from "viem";
|
2
|
+
import { getBalance, sendRawTransaction, sendTransaction, waitForTransactionReceipt } from "viem/actions";
|
3
3
|
import deployment from "./create2/deployment.json";
|
4
4
|
import { debug } from "./debug";
|
5
|
+
import { getDeployer } from "./getDeployer";
|
5
6
|
|
6
7
|
const deployer = `0x${deployment.address}` as const;
|
7
8
|
|
8
9
|
export async function ensureDeployer(client: Client<Transport, Chain | undefined, Account>): Promise<Address> {
|
9
|
-
const
|
10
|
-
if (
|
11
|
-
|
12
|
-
// check if deployed bytecode is the same as the expected bytecode (minus 14-bytes creation code prefix)
|
13
|
-
if (bytecode !== sliceHex(`0x${deployment.creationCode}`, 14)) {
|
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;
|
10
|
+
const existingDeployer = await getDeployer(client);
|
11
|
+
if (existingDeployer !== undefined) {
|
12
|
+
return existingDeployer;
|
19
13
|
}
|
20
14
|
|
21
15
|
// There's not really a way to simulate a pre-EIP-155 (no chain ID) transaction,
|
@@ -1,105 +1,30 @@
|
|
1
|
-
import
|
2
|
-
import balanceTransferSystemBuild from "@latticexyz/world/out/BalanceTransferSystem.sol/BalanceTransferSystem.json" assert { type: "json" };
|
3
|
-
import batchCallSystemBuild from "@latticexyz/world/out/BatchCallSystem.sol/BatchCallSystem.json" assert { type: "json" };
|
4
|
-
import registrationSystemBuild from "@latticexyz/world/out/RegistrationSystem.sol/RegistrationSystem.json" assert { type: "json" };
|
5
|
-
import initModuleBuild from "@latticexyz/world/out/InitModule.sol/InitModule.json" assert { type: "json" };
|
6
|
-
import initModuleAbi from "@latticexyz/world/out/InitModule.sol/InitModule.abi.json" assert { type: "json" };
|
7
|
-
import worldFactoryBuild from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.json" assert { type: "json" };
|
8
|
-
import worldFactoryAbi from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.abi.json" assert { type: "json" };
|
9
|
-
import { Client, Transport, Chain, Account, Hex, getCreate2Address, encodeDeployData, size, Address } from "viem";
|
10
|
-
import { salt } from "./common";
|
1
|
+
import { Client, Transport, Chain, Account, Hex, Address } from "viem";
|
11
2
|
import { ensureContractsDeployed } from "./ensureContractsDeployed";
|
12
|
-
import {
|
3
|
+
import { getWorldFactoryContracts } from "./getWorldFactoryContracts";
|
4
|
+
import { getWorldProxyFactoryContracts } from "./getWorldProxyFactoryContracts";
|
13
5
|
|
14
6
|
export async function ensureWorldFactory(
|
15
7
|
client: Client<Transport, Chain | undefined, Account>,
|
16
8
|
deployerAddress: Hex,
|
9
|
+
withWorldProxy?: boolean,
|
17
10
|
): Promise<Address> {
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
salt,
|
32
|
-
});
|
33
|
-
|
34
|
-
const batchCallSystemDeployedBytecodeSize = size(batchCallSystemBuild.deployedBytecode.object as Hex);
|
35
|
-
const batchCallSystemBytecode = batchCallSystemBuild.bytecode.object as Hex;
|
36
|
-
const batchCallSystem = getCreate2Address({ from: deployerAddress, bytecode: batchCallSystemBytecode, salt });
|
37
|
-
|
38
|
-
const registrationDeployedBytecodeSize = size(registrationSystemBuild.deployedBytecode.object as Hex);
|
39
|
-
const registrationBytecode = registrationSystemBuild.bytecode.object as Hex;
|
40
|
-
const registration = getCreate2Address({
|
41
|
-
from: deployerAddress,
|
42
|
-
bytecode: registrationBytecode,
|
43
|
-
salt,
|
44
|
-
});
|
45
|
-
|
46
|
-
const initModuleDeployedBytecodeSize = size(initModuleBuild.deployedBytecode.object as Hex);
|
47
|
-
const initModuleBytecode = encodeDeployData({
|
48
|
-
bytecode: initModuleBuild.bytecode.object as Hex,
|
49
|
-
abi: initModuleAbi,
|
50
|
-
args: [accessManagementSystem, balanceTransferSystem, batchCallSystem, registration],
|
51
|
-
});
|
52
|
-
|
53
|
-
const initModule = getCreate2Address({ from: deployerAddress, bytecode: initModuleBytecode, salt });
|
54
|
-
|
55
|
-
const worldFactoryDeployedBytecodeSize = size(worldFactoryBuild.deployedBytecode.object as Hex);
|
56
|
-
const worldFactoryBytecode = encodeDeployData({
|
57
|
-
bytecode: worldFactoryBuild.bytecode.object as Hex,
|
58
|
-
abi: worldFactoryAbi,
|
59
|
-
args: [initModule],
|
60
|
-
});
|
61
|
-
|
62
|
-
const worldFactory = getCreate2Address({ from: deployerAddress, bytecode: worldFactoryBytecode, salt });
|
63
|
-
|
64
|
-
const worldFactoryContracts: readonly Contract[] = [
|
65
|
-
{
|
66
|
-
bytecode: accessManagementSystemBytecode,
|
67
|
-
deployedBytecodeSize: accessManagementSystemDeployedBytecodeSize,
|
68
|
-
label: "access management system",
|
69
|
-
},
|
70
|
-
{
|
71
|
-
bytecode: balanceTransferSystemBytecode,
|
72
|
-
deployedBytecodeSize: balanceTransferSystemDeployedBytecodeSize,
|
73
|
-
label: "balance transfer system",
|
74
|
-
},
|
75
|
-
{
|
76
|
-
bytecode: batchCallSystemBytecode,
|
77
|
-
deployedBytecodeSize: batchCallSystemDeployedBytecodeSize,
|
78
|
-
label: "batch call system",
|
79
|
-
},
|
80
|
-
{
|
81
|
-
bytecode: registrationBytecode,
|
82
|
-
deployedBytecodeSize: registrationDeployedBytecodeSize,
|
83
|
-
label: "core registration system",
|
84
|
-
},
|
85
|
-
{
|
86
|
-
bytecode: initModuleBytecode,
|
87
|
-
deployedBytecodeSize: initModuleDeployedBytecodeSize,
|
88
|
-
label: "core module",
|
89
|
-
},
|
90
|
-
{
|
91
|
-
bytecode: worldFactoryBytecode,
|
92
|
-
deployedBytecodeSize: worldFactoryDeployedBytecodeSize,
|
93
|
-
label: "world factory",
|
94
|
-
},
|
95
|
-
];
|
96
|
-
|
97
|
-
// WorldFactory constructor doesn't call InitModule, only sets its address, so we can do these in parallel since the address is deterministic
|
11
|
+
if (withWorldProxy) {
|
12
|
+
const contracts = getWorldProxyFactoryContracts(deployerAddress);
|
13
|
+
// We can deploy these contracts in parallel because they do not call each other at this point.
|
14
|
+
await ensureContractsDeployed({
|
15
|
+
client,
|
16
|
+
deployerAddress,
|
17
|
+
contracts: Object.values(contracts),
|
18
|
+
});
|
19
|
+
return contracts.WorldProxyFactory.address;
|
20
|
+
}
|
21
|
+
|
22
|
+
const contracts = getWorldFactoryContracts(deployerAddress);
|
23
|
+
// We can deploy these contracts in parallel because they do not call each other at this point.
|
98
24
|
await ensureContractsDeployed({
|
99
25
|
client,
|
100
26
|
deployerAddress,
|
101
|
-
contracts:
|
27
|
+
contracts: Object.values(contracts),
|
102
28
|
});
|
103
|
-
|
104
|
-
return worldFactory;
|
29
|
+
return contracts.WorldFactory.address;
|
105
30
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { Address, Chain, Client, Transport, sliceHex } from "viem";
|
2
|
+
import { getBytecode } from "viem/actions";
|
3
|
+
import deployment from "./create2/deployment.json";
|
4
|
+
import { debug } from "./debug";
|
5
|
+
|
6
|
+
const deployer = `0x${deployment.address}` as const;
|
7
|
+
|
8
|
+
export async function getDeployer(client: Client<Transport, Chain | undefined>): Promise<Address | undefined> {
|
9
|
+
const bytecode = await getBytecode(client, { address: deployer });
|
10
|
+
if (bytecode) {
|
11
|
+
debug("found CREATE2 deployer at", deployer);
|
12
|
+
// check if deployed bytecode is the same as the expected bytecode (minus 14-bytes creation code prefix)
|
13
|
+
if (bytecode !== sliceHex(`0x${deployment.creationCode}`, 14)) {
|
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;
|
19
|
+
}
|
20
|
+
}
|
@@ -1,8 +1,9 @@
|
|
1
|
-
import { Client, parseAbiItem, Hex } from "viem";
|
1
|
+
import { Client, parseAbiItem, Hex, HttpRequestError } from "viem";
|
2
2
|
import { getLogs } from "viem/actions";
|
3
3
|
import { storeSpliceStaticDataEvent } from "@latticexyz/store";
|
4
4
|
import { WorldDeploy, storeTables } from "./common";
|
5
5
|
import { debug } from "./debug";
|
6
|
+
import pRetry from "p-retry";
|
6
7
|
|
7
8
|
export async function getResourceIds({
|
8
9
|
client,
|
@@ -15,15 +16,28 @@ export async function getResourceIds({
|
|
15
16
|
// TODO: PR to viem's getLogs to accept topics array so we can filter on all store events and quickly recreate this table's current state
|
16
17
|
|
17
18
|
debug("looking up resource IDs for", worldDeploy.address);
|
18
|
-
const logs = await
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
const logs = await pRetry(
|
20
|
+
() =>
|
21
|
+
getLogs(client, {
|
22
|
+
strict: true,
|
23
|
+
address: worldDeploy.address,
|
24
|
+
fromBlock: worldDeploy.deployBlock,
|
25
|
+
toBlock: worldDeploy.stateBlock,
|
26
|
+
event: parseAbiItem(storeSpliceStaticDataEvent),
|
27
|
+
args: { tableId: storeTables.store_ResourceIds.tableId },
|
28
|
+
}),
|
29
|
+
{
|
30
|
+
retries: 3,
|
31
|
+
onFailedAttempt: async (error) => {
|
32
|
+
const shouldRetry =
|
33
|
+
error instanceof HttpRequestError && error.status === 400 && error.message.includes("block is out of range");
|
26
34
|
|
35
|
+
if (!shouldRetry) {
|
36
|
+
throw error;
|
37
|
+
}
|
38
|
+
},
|
39
|
+
},
|
40
|
+
);
|
27
41
|
const resourceIds = logs.map((log) => log.args.keyTuple[0]);
|
28
42
|
debug("found", resourceIds.length, "resource IDs for", worldDeploy.address);
|
29
43
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import accessManagementSystemBuild from "@latticexyz/world/out/AccessManagementSystem.sol/AccessManagementSystem.json" assert { type: "json" };
|
2
|
+
import balanceTransferSystemBuild from "@latticexyz/world/out/BalanceTransferSystem.sol/BalanceTransferSystem.json" assert { type: "json" };
|
3
|
+
import batchCallSystemBuild from "@latticexyz/world/out/BatchCallSystem.sol/BatchCallSystem.json" assert { type: "json" };
|
4
|
+
import registrationSystemBuild from "@latticexyz/world/out/RegistrationSystem.sol/RegistrationSystem.json" assert { type: "json" };
|
5
|
+
import initModuleBuild from "@latticexyz/world/out/InitModule.sol/InitModule.json" assert { type: "json" };
|
6
|
+
import initModuleAbi from "@latticexyz/world/out/InitModule.sol/InitModule.abi.json" assert { type: "json" };
|
7
|
+
import { Hex, getCreate2Address, encodeDeployData, size } from "viem";
|
8
|
+
import { salt } from "./common";
|
9
|
+
|
10
|
+
export function getWorldContracts(deployerAddress: Hex) {
|
11
|
+
const accessManagementSystemDeployedBytecodeSize = size(accessManagementSystemBuild.deployedBytecode.object as Hex);
|
12
|
+
const accessManagementSystemBytecode = accessManagementSystemBuild.bytecode.object as Hex;
|
13
|
+
const accessManagementSystem = getCreate2Address({
|
14
|
+
from: deployerAddress,
|
15
|
+
bytecode: accessManagementSystemBytecode,
|
16
|
+
salt,
|
17
|
+
});
|
18
|
+
|
19
|
+
const balanceTransferSystemDeployedBytecodeSize = size(balanceTransferSystemBuild.deployedBytecode.object as Hex);
|
20
|
+
const balanceTransferSystemBytecode = balanceTransferSystemBuild.bytecode.object as Hex;
|
21
|
+
const balanceTransferSystem = getCreate2Address({
|
22
|
+
from: deployerAddress,
|
23
|
+
bytecode: balanceTransferSystemBytecode,
|
24
|
+
salt,
|
25
|
+
});
|
26
|
+
|
27
|
+
const batchCallSystemDeployedBytecodeSize = size(batchCallSystemBuild.deployedBytecode.object as Hex);
|
28
|
+
const batchCallSystemBytecode = batchCallSystemBuild.bytecode.object as Hex;
|
29
|
+
const batchCallSystem = getCreate2Address({ from: deployerAddress, bytecode: batchCallSystemBytecode, salt });
|
30
|
+
|
31
|
+
const registrationDeployedBytecodeSize = size(registrationSystemBuild.deployedBytecode.object as Hex);
|
32
|
+
const registrationBytecode = registrationSystemBuild.bytecode.object as Hex;
|
33
|
+
const registration = getCreate2Address({
|
34
|
+
from: deployerAddress,
|
35
|
+
bytecode: registrationBytecode,
|
36
|
+
salt,
|
37
|
+
});
|
38
|
+
|
39
|
+
const initModuleDeployedBytecodeSize = size(initModuleBuild.deployedBytecode.object as Hex);
|
40
|
+
const initModuleBytecode = encodeDeployData({
|
41
|
+
bytecode: initModuleBuild.bytecode.object as Hex,
|
42
|
+
abi: initModuleAbi,
|
43
|
+
args: [accessManagementSystem, balanceTransferSystem, batchCallSystem, registration],
|
44
|
+
});
|
45
|
+
const initModule = getCreate2Address({ from: deployerAddress, bytecode: initModuleBytecode, salt });
|
46
|
+
|
47
|
+
return {
|
48
|
+
AccessManagementSystem: {
|
49
|
+
bytecode: accessManagementSystemBytecode,
|
50
|
+
deployedBytecodeSize: accessManagementSystemDeployedBytecodeSize,
|
51
|
+
label: "access management system",
|
52
|
+
address: accessManagementSystem,
|
53
|
+
},
|
54
|
+
BalanceTransferSystem: {
|
55
|
+
bytecode: balanceTransferSystemBytecode,
|
56
|
+
deployedBytecodeSize: balanceTransferSystemDeployedBytecodeSize,
|
57
|
+
label: "balance transfer system",
|
58
|
+
address: balanceTransferSystem,
|
59
|
+
},
|
60
|
+
BatchCallSystem: {
|
61
|
+
bytecode: batchCallSystemBytecode,
|
62
|
+
deployedBytecodeSize: batchCallSystemDeployedBytecodeSize,
|
63
|
+
label: "batch call system",
|
64
|
+
address: batchCallSystem,
|
65
|
+
},
|
66
|
+
RegistrationSystem: {
|
67
|
+
bytecode: registrationBytecode,
|
68
|
+
deployedBytecodeSize: registrationDeployedBytecodeSize,
|
69
|
+
label: "core registration system",
|
70
|
+
address: registration,
|
71
|
+
},
|
72
|
+
InitModule: {
|
73
|
+
bytecode: initModuleBytecode,
|
74
|
+
deployedBytecodeSize: initModuleDeployedBytecodeSize,
|
75
|
+
label: "core module",
|
76
|
+
address: initModule,
|
77
|
+
},
|
78
|
+
};
|
79
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import worldFactoryBuild from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.json" assert { type: "json" };
|
2
|
+
import worldFactoryAbi from "@latticexyz/world/out/WorldFactory.sol/WorldFactory.abi.json" assert { type: "json" };
|
3
|
+
import { Hex, getCreate2Address, encodeDeployData, size } from "viem";
|
4
|
+
import { salt } from "./common";
|
5
|
+
import { getWorldContracts } from "./getWorldContracts";
|
6
|
+
|
7
|
+
export function getWorldFactoryContracts(deployerAddress: Hex) {
|
8
|
+
const worldContracts = getWorldContracts(deployerAddress);
|
9
|
+
|
10
|
+
const worldFactoryDeployedBytecodeSize = size(worldFactoryBuild.deployedBytecode.object as Hex);
|
11
|
+
const worldFactoryBytecode = encodeDeployData({
|
12
|
+
bytecode: worldFactoryBuild.bytecode.object as Hex,
|
13
|
+
abi: worldFactoryAbi,
|
14
|
+
args: [worldContracts.InitModule.address],
|
15
|
+
});
|
16
|
+
const worldFactory = getCreate2Address({ from: deployerAddress, bytecode: worldFactoryBytecode, salt });
|
17
|
+
|
18
|
+
return {
|
19
|
+
...worldContracts,
|
20
|
+
WorldFactory: {
|
21
|
+
bytecode: worldFactoryBytecode,
|
22
|
+
deployedBytecodeSize: worldFactoryDeployedBytecodeSize,
|
23
|
+
label: "world factory",
|
24
|
+
address: worldFactory,
|
25
|
+
},
|
26
|
+
};
|
27
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import worldProxyFactoryBuild from "@latticexyz/world/out/WorldProxyFactory.sol/WorldProxyFactory.json" assert { type: "json" };
|
2
|
+
import worldProxyFactoryAbi from "@latticexyz/world/out/WorldProxyFactory.sol/WorldProxyFactory.abi.json" assert { type: "json" };
|
3
|
+
import { Hex, getCreate2Address, encodeDeployData, size } from "viem";
|
4
|
+
import { salt } from "./common";
|
5
|
+
import { getWorldContracts } from "./getWorldContracts";
|
6
|
+
|
7
|
+
export function getWorldProxyFactoryContracts(deployerAddress: Hex) {
|
8
|
+
const worldContracts = getWorldContracts(deployerAddress);
|
9
|
+
|
10
|
+
const worldProxyFactoryDeployedBytecodeSize = size(worldProxyFactoryBuild.deployedBytecode.object as Hex);
|
11
|
+
const worldProxyFactoryBytecode = encodeDeployData({
|
12
|
+
bytecode: worldProxyFactoryBuild.bytecode.object as Hex,
|
13
|
+
abi: worldProxyFactoryAbi,
|
14
|
+
args: [worldContracts.InitModule.address],
|
15
|
+
});
|
16
|
+
const worldProxyFactory = getCreate2Address({ from: deployerAddress, bytecode: worldProxyFactoryBytecode, salt });
|
17
|
+
|
18
|
+
return {
|
19
|
+
...worldContracts,
|
20
|
+
WorldProxyFactory: {
|
21
|
+
bytecode: worldProxyFactoryBytecode,
|
22
|
+
deployedBytecodeSize: worldProxyFactoryDeployedBytecodeSize,
|
23
|
+
label: "world proxy factory",
|
24
|
+
address: worldProxyFactory,
|
25
|
+
},
|
26
|
+
};
|
27
|
+
}
|
package/src/runDeploy.ts
CHANGED
@@ -17,7 +17,7 @@ import { WorldDeploy } from "./deploy/common";
|
|
17
17
|
import { build } from "./build";
|
18
18
|
|
19
19
|
export const deployOptions = {
|
20
|
-
configPath: { type: "string", desc: "Path to the config file" },
|
20
|
+
configPath: { type: "string", desc: "Path to the MUD config file" },
|
21
21
|
printConfig: { type: "boolean", desc: "Print the resolved config" },
|
22
22
|
profile: { type: "string", desc: "The foundry profile to use" },
|
23
23
|
saveDeployment: { type: "boolean", desc: "Save the deployment info to a file", default: true },
|
@@ -37,6 +37,7 @@ export const deployOptions = {
|
|
37
37
|
type: "boolean",
|
38
38
|
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.",
|
39
39
|
},
|
40
|
+
forgeScriptOptions: { type: "string", description: "Options to pass to forge script PostDeploy.s.sol" },
|
40
41
|
salt: {
|
41
42
|
type: "string",
|
42
43
|
desc: "The deployment salt to use. Defaults to a random salt.",
|
@@ -110,9 +111,10 @@ in your contracts directory to use the default anvil private key.`,
|
|
110
111
|
worldAddress: opts.worldAddress as Hex | undefined,
|
111
112
|
client,
|
112
113
|
config: resolvedConfig,
|
114
|
+
withWorldProxy: configV2.deploy.useProxy,
|
113
115
|
});
|
114
116
|
if (opts.worldAddress == null || opts.alwaysRunPostDeploy) {
|
115
|
-
await postDeploy(config.postDeployScript, worldDeploy.address, rpc, profile);
|
117
|
+
await postDeploy(config.postDeployScript, worldDeploy.address, rpc, profile, opts.forgeScriptOptions);
|
116
118
|
}
|
117
119
|
console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds"));
|
118
120
|
|
package/src/utils/postDeploy.ts
CHANGED
@@ -2,24 +2,48 @@ import { existsSync } from "fs";
|
|
2
2
|
import path from "path";
|
3
3
|
import chalk from "chalk";
|
4
4
|
import { getScriptDirectory, forge } from "@latticexyz/common/foundry";
|
5
|
+
import { execSync } from "child_process";
|
6
|
+
import { formatGwei } from "viem";
|
5
7
|
|
6
8
|
export async function postDeploy(
|
7
9
|
postDeployScript: string,
|
8
10
|
worldAddress: string,
|
9
11
|
rpc: string,
|
10
12
|
profile: string | undefined,
|
13
|
+
forgeOptions: string | undefined,
|
11
14
|
): Promise<void> {
|
12
|
-
//
|
15
|
+
// TODO: make this more robust as it is likely to fail for any args that have a space in them
|
16
|
+
const userOptions = forgeOptions?.replaceAll("\\", "").split(" ") ?? [];
|
13
17
|
const postDeployPath = path.join(await getScriptDirectory(), postDeployScript + ".s.sol");
|
14
|
-
if (existsSync(postDeployPath)) {
|
15
|
-
console.log(chalk.blue(`Executing post deploy script at ${postDeployPath}`));
|
16
|
-
await forge(
|
17
|
-
["script", postDeployScript, "--sig", "run(address)", worldAddress, "--broadcast", "--rpc-url", rpc, "-vvv"],
|
18
|
-
{
|
19
|
-
profile: profile,
|
20
|
-
},
|
21
|
-
);
|
22
|
-
} else {
|
18
|
+
if (!existsSync(postDeployPath)) {
|
23
19
|
console.log(`No script at ${postDeployPath}, skipping post deploy hook`);
|
20
|
+
return;
|
24
21
|
}
|
22
|
+
|
23
|
+
// TODO: replace this with a viem call
|
24
|
+
const gasPrice = BigInt(execSync(`cast gas-price --rpc-url ${rpc}`, { encoding: "utf-8" }).trim());
|
25
|
+
|
26
|
+
console.log(chalk.blue(`Executing post deploy script at ${postDeployPath}`));
|
27
|
+
console.log(chalk.blue(` using gas price of ${formatGwei(gasPrice)} gwei`));
|
28
|
+
|
29
|
+
await forge(
|
30
|
+
[
|
31
|
+
"script",
|
32
|
+
postDeployScript,
|
33
|
+
"--broadcast",
|
34
|
+
"--sig",
|
35
|
+
"run(address)",
|
36
|
+
worldAddress,
|
37
|
+
// TODO: also set priority fee?
|
38
|
+
"--with-gas-price",
|
39
|
+
gasPrice.toString(),
|
40
|
+
"--rpc-url",
|
41
|
+
rpc,
|
42
|
+
"-vvv",
|
43
|
+
...userOptions,
|
44
|
+
],
|
45
|
+
{
|
46
|
+
profile: profile,
|
47
|
+
},
|
48
|
+
);
|
25
49
|
}
|