@latticexyz/cli 2.0.0-snapshot-test-32d38619 → 2.0.0
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-QXUPZVZL.js +4 -0
- 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/errors-MZURIB7V.js.map +1 -0
- package/dist/index.js +0 -1
- package/dist/mud.js +1 -14
- package/dist/mud.js.map +1 -1
- package/package.json +21 -15
- package/src/build.ts +48 -0
- package/src/commands/build.ts +35 -0
- package/src/commands/deploy.ts +8 -31
- package/src/commands/dev-contracts.ts +80 -141
- package/src/commands/index.ts +2 -0
- package/src/commands/set-version.ts +24 -62
- package/src/commands/tablegen.ts +2 -2
- package/src/commands/test.ts +30 -36
- package/src/commands/trace.ts +15 -11
- package/src/commands/worldgen.ts +7 -6
- package/src/common.ts +1 -0
- package/src/debug.ts +10 -0
- package/src/deploy/common.ts +122 -0
- package/src/deploy/configToTables.ts +70 -0
- package/src/deploy/create2/README.md +13 -0
- package/src/deploy/create2/deployment.json +8 -0
- package/src/deploy/createPrepareDeploy.ts +28 -0
- package/src/deploy/debug.ts +10 -0
- package/src/deploy/deploy.ts +133 -0
- package/src/deploy/deployWorld.ts +38 -0
- package/src/deploy/ensureContract.ts +66 -0
- package/src/deploy/ensureContractsDeployed.ts +33 -0
- package/src/deploy/ensureDeployer.ts +75 -0
- package/src/deploy/ensureFunctions.ts +86 -0
- package/src/deploy/ensureModules.ts +79 -0
- package/src/deploy/ensureNamespaceOwner.ts +71 -0
- package/src/deploy/ensureSystems.ts +187 -0
- package/src/deploy/ensureTables.ts +64 -0
- package/src/deploy/ensureWorldFactory.ts +105 -0
- package/src/deploy/findLibraries.ts +36 -0
- package/src/deploy/getFunctions.ts +58 -0
- package/src/deploy/getResourceAccess.ts +51 -0
- package/src/deploy/getResourceIds.ts +31 -0
- package/src/deploy/getSystems.ts +47 -0
- package/src/deploy/getTableValue.ts +30 -0
- package/src/deploy/getTables.ts +59 -0
- package/src/deploy/getWorldDeploy.ts +39 -0
- package/src/deploy/logsToWorldDeploy.ts +49 -0
- package/src/deploy/orderByDependencies.ts +12 -0
- package/src/deploy/resolveConfig.ts +148 -0
- package/src/index.ts +1 -1
- package/src/mud.ts +37 -31
- package/src/mudPackages.ts +24 -0
- package/src/runDeploy.ts +149 -0
- package/src/utils/defaultModuleContracts.ts +30 -0
- package/src/utils/errors.ts +1 -1
- package/src/utils/findPlaceholders.ts +27 -0
- package/src/utils/getContractData.ts +38 -0
- package/src/utils/{utils/postDeploy.ts → postDeploy.ts} +2 -2
- package/src/utils/printMUD.ts +1 -1
- package/dist/chunk-WERDORTY.js +0 -11
- package/dist/chunk-WERDORTY.js.map +0 -1
- package/src/utils/deploy.ts +0 -255
- package/src/utils/deployHandler.ts +0 -93
- package/src/utils/modules/constants.ts +0 -23
- package/src/utils/modules/getInstallModuleCallData.ts +0 -27
- package/src/utils/modules/getUserModules.ts +0 -5
- package/src/utils/modules/types.ts +0 -14
- package/src/utils/systems/getGrantAccessCallData.ts +0 -29
- package/src/utils/systems/getRegisterFunctionSelectorsCallData.ts +0 -57
- package/src/utils/systems/getRegisterSystemCallData.ts +0 -17
- package/src/utils/systems/types.ts +0 -9
- package/src/utils/systems/utils.ts +0 -42
- package/src/utils/tables/getRegisterTableCallData.ts +0 -49
- package/src/utils/tables/getTableIds.ts +0 -21
- package/src/utils/tables/types.ts +0 -12
- package/src/utils/utils/confirmNonce.ts +0 -24
- package/src/utils/utils/deployContract.ts +0 -33
- package/src/utils/utils/fastTxExecute.ts +0 -56
- package/src/utils/utils/getChainId.ts +0 -10
- package/src/utils/utils/getContractData.ts +0 -29
- package/src/utils/utils/setInternalFeePerGas.ts +0 -49
- package/src/utils/utils/toBytes16.ts +0 -16
- package/src/utils/utils/types.ts +0 -21
- package/src/utils/world.ts +0 -28
@@ -0,0 +1,187 @@
|
|
1
|
+
import { Client, Transport, Chain, Account, Hex, getAddress, Address } from "viem";
|
2
|
+
import { writeContract, resourceToLabel } from "@latticexyz/common";
|
3
|
+
import { Library, System, WorldDeploy, worldAbi } from "./common";
|
4
|
+
import { debug } from "./debug";
|
5
|
+
import { getSystems } from "./getSystems";
|
6
|
+
import { getResourceAccess } from "./getResourceAccess";
|
7
|
+
import { wait } from "@latticexyz/common/utils";
|
8
|
+
import pRetry from "p-retry";
|
9
|
+
import { ensureContractsDeployed } from "./ensureContractsDeployed";
|
10
|
+
|
11
|
+
// TODO: move each system registration+access to batch call to be atomic
|
12
|
+
|
13
|
+
export async function ensureSystems({
|
14
|
+
client,
|
15
|
+
deployerAddress,
|
16
|
+
libraries,
|
17
|
+
worldDeploy,
|
18
|
+
systems,
|
19
|
+
}: {
|
20
|
+
readonly client: Client<Transport, Chain | undefined, Account>;
|
21
|
+
readonly deployerAddress: Hex;
|
22
|
+
readonly libraries: readonly Library[];
|
23
|
+
readonly worldDeploy: WorldDeploy;
|
24
|
+
readonly systems: readonly System[];
|
25
|
+
}): Promise<readonly Hex[]> {
|
26
|
+
const [worldSystems, worldAccess] = await Promise.all([
|
27
|
+
getSystems({ client, worldDeploy }),
|
28
|
+
getResourceAccess({ client, worldDeploy }),
|
29
|
+
]);
|
30
|
+
|
31
|
+
// Register or replace systems
|
32
|
+
|
33
|
+
const existingSystems = systems.filter((system) =>
|
34
|
+
worldSystems.some(
|
35
|
+
(worldSystem) =>
|
36
|
+
worldSystem.systemId === system.systemId &&
|
37
|
+
getAddress(worldSystem.address) === getAddress(system.prepareDeploy(deployerAddress, libraries).address),
|
38
|
+
),
|
39
|
+
);
|
40
|
+
if (existingSystems.length) {
|
41
|
+
debug("existing systems", existingSystems.map(resourceToLabel).join(", "));
|
42
|
+
}
|
43
|
+
const existingSystemIds = existingSystems.map((system) => system.systemId);
|
44
|
+
|
45
|
+
const missingSystems = systems.filter((system) => !existingSystemIds.includes(system.systemId));
|
46
|
+
if (!missingSystems.length) return [];
|
47
|
+
|
48
|
+
const systemsToUpgrade = missingSystems.filter((system) =>
|
49
|
+
worldSystems.some(
|
50
|
+
(worldSystem) =>
|
51
|
+
worldSystem.systemId === system.systemId &&
|
52
|
+
getAddress(worldSystem.address) !== getAddress(system.prepareDeploy(deployerAddress, libraries).address),
|
53
|
+
),
|
54
|
+
);
|
55
|
+
if (systemsToUpgrade.length) {
|
56
|
+
debug("upgrading systems", systemsToUpgrade.map(resourceToLabel).join(", "));
|
57
|
+
}
|
58
|
+
|
59
|
+
const systemsToAdd = missingSystems.filter(
|
60
|
+
(system) => !worldSystems.some((worldSystem) => worldSystem.systemId === system.systemId),
|
61
|
+
);
|
62
|
+
if (systemsToAdd.length) {
|
63
|
+
debug("registering new systems", systemsToAdd.map(resourceToLabel).join(", "));
|
64
|
+
}
|
65
|
+
|
66
|
+
await ensureContractsDeployed({
|
67
|
+
client,
|
68
|
+
deployerAddress,
|
69
|
+
contracts: missingSystems.map((system) => ({
|
70
|
+
bytecode: system.prepareDeploy(deployerAddress, libraries).bytecode,
|
71
|
+
deployedBytecodeSize: system.deployedBytecodeSize,
|
72
|
+
label: `${resourceToLabel(system)} system`,
|
73
|
+
})),
|
74
|
+
});
|
75
|
+
|
76
|
+
const registerTxs = await Promise.all(
|
77
|
+
missingSystems.map((system) =>
|
78
|
+
pRetry(
|
79
|
+
() =>
|
80
|
+
writeContract(client, {
|
81
|
+
chain: client.chain ?? null,
|
82
|
+
address: worldDeploy.address,
|
83
|
+
abi: worldAbi,
|
84
|
+
// TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
|
85
|
+
functionName: "registerSystem",
|
86
|
+
args: [system.systemId, system.prepareDeploy(deployerAddress, libraries).address, system.allowAll],
|
87
|
+
}),
|
88
|
+
{
|
89
|
+
retries: 3,
|
90
|
+
onFailedAttempt: async (error) => {
|
91
|
+
const delay = error.attemptNumber * 500;
|
92
|
+
debug(`failed to register system ${resourceToLabel(system)}, retrying in ${delay}ms...`);
|
93
|
+
await wait(delay);
|
94
|
+
},
|
95
|
+
},
|
96
|
+
),
|
97
|
+
),
|
98
|
+
);
|
99
|
+
|
100
|
+
// Adjust system access
|
101
|
+
|
102
|
+
const systemIds = systems.map((system) => system.systemId);
|
103
|
+
const currentAccess = worldAccess.filter(({ resourceId }) => systemIds.includes(resourceId));
|
104
|
+
const desiredAccess = [
|
105
|
+
...systems.flatMap((system) =>
|
106
|
+
system.allowedAddresses.map((address) => ({ resourceId: system.systemId, address })),
|
107
|
+
),
|
108
|
+
...systems.flatMap((system) =>
|
109
|
+
system.allowedSystemIds
|
110
|
+
.map((systemId) => ({
|
111
|
+
resourceId: system.systemId,
|
112
|
+
address:
|
113
|
+
worldSystems.find((s) => s.systemId === systemId)?.address ??
|
114
|
+
systems.find((s) => s.systemId === systemId)?.prepareDeploy(deployerAddress, libraries).address,
|
115
|
+
}))
|
116
|
+
.filter((access): access is typeof access & { address: Address } => access.address != null),
|
117
|
+
),
|
118
|
+
];
|
119
|
+
|
120
|
+
const accessToAdd = desiredAccess.filter(
|
121
|
+
(access) =>
|
122
|
+
!currentAccess.some(
|
123
|
+
({ resourceId, address }) =>
|
124
|
+
resourceId === access.resourceId && getAddress(address) === getAddress(access.address),
|
125
|
+
),
|
126
|
+
);
|
127
|
+
|
128
|
+
const accessToRemove = currentAccess.filter(
|
129
|
+
(access) =>
|
130
|
+
!desiredAccess.some(
|
131
|
+
({ resourceId, address }) =>
|
132
|
+
resourceId === access.resourceId && getAddress(address) === getAddress(access.address),
|
133
|
+
),
|
134
|
+
);
|
135
|
+
|
136
|
+
if (accessToRemove.length) {
|
137
|
+
debug("revoking", accessToRemove.length, "access grants");
|
138
|
+
}
|
139
|
+
if (accessToAdd.length) {
|
140
|
+
debug("adding", accessToAdd.length, "access grants");
|
141
|
+
}
|
142
|
+
|
143
|
+
const accessTxs = await Promise.all([
|
144
|
+
...accessToRemove.map((access) =>
|
145
|
+
pRetry(
|
146
|
+
() =>
|
147
|
+
writeContract(client, {
|
148
|
+
chain: client.chain ?? null,
|
149
|
+
address: worldDeploy.address,
|
150
|
+
abi: worldAbi,
|
151
|
+
functionName: "revokeAccess",
|
152
|
+
args: [access.resourceId, access.address],
|
153
|
+
}),
|
154
|
+
{
|
155
|
+
retries: 3,
|
156
|
+
onFailedAttempt: async (error) => {
|
157
|
+
const delay = error.attemptNumber * 500;
|
158
|
+
debug(`failed to revoke access, retrying in ${delay}ms...`);
|
159
|
+
await wait(delay);
|
160
|
+
},
|
161
|
+
},
|
162
|
+
),
|
163
|
+
),
|
164
|
+
...accessToAdd.map((access) =>
|
165
|
+
pRetry(
|
166
|
+
() =>
|
167
|
+
writeContract(client, {
|
168
|
+
chain: client.chain ?? null,
|
169
|
+
address: worldDeploy.address,
|
170
|
+
abi: worldAbi,
|
171
|
+
functionName: "grantAccess",
|
172
|
+
args: [access.resourceId, access.address],
|
173
|
+
}),
|
174
|
+
{
|
175
|
+
retries: 3,
|
176
|
+
onFailedAttempt: async (error) => {
|
177
|
+
const delay = error.attemptNumber * 500;
|
178
|
+
debug(`failed to grant access, retrying in ${delay}ms...`);
|
179
|
+
await wait(delay);
|
180
|
+
},
|
181
|
+
},
|
182
|
+
),
|
183
|
+
),
|
184
|
+
]);
|
185
|
+
|
186
|
+
return [...registerTxs, ...accessTxs];
|
187
|
+
}
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import { Client, Transport, Chain, Account, Hex } from "viem";
|
2
|
+
import { Table } from "./configToTables";
|
3
|
+
import { resourceToLabel, writeContract } from "@latticexyz/common";
|
4
|
+
import { WorldDeploy, worldAbi } from "./common";
|
5
|
+
import { valueSchemaToFieldLayoutHex, keySchemaToHex, valueSchemaToHex } from "@latticexyz/protocol-parser/internal";
|
6
|
+
import { debug } from "./debug";
|
7
|
+
import { getTables } from "./getTables";
|
8
|
+
import pRetry from "p-retry";
|
9
|
+
import { wait } from "@latticexyz/common/utils";
|
10
|
+
|
11
|
+
export async function ensureTables({
|
12
|
+
client,
|
13
|
+
worldDeploy,
|
14
|
+
tables,
|
15
|
+
}: {
|
16
|
+
readonly client: Client<Transport, Chain | undefined, Account>;
|
17
|
+
readonly worldDeploy: WorldDeploy;
|
18
|
+
readonly tables: readonly Table[];
|
19
|
+
}): Promise<readonly Hex[]> {
|
20
|
+
const worldTables = await getTables({ client, worldDeploy });
|
21
|
+
const worldTableIds = worldTables.map((table) => table.tableId);
|
22
|
+
|
23
|
+
const existingTables = tables.filter((table) => worldTableIds.includes(table.tableId));
|
24
|
+
if (existingTables.length) {
|
25
|
+
debug("existing tables", existingTables.map(resourceToLabel).join(", "));
|
26
|
+
}
|
27
|
+
|
28
|
+
const missingTables = tables.filter((table) => !worldTableIds.includes(table.tableId));
|
29
|
+
if (missingTables.length) {
|
30
|
+
debug("registering tables", missingTables.map(resourceToLabel).join(", "));
|
31
|
+
return await Promise.all(
|
32
|
+
missingTables.map((table) =>
|
33
|
+
pRetry(
|
34
|
+
() =>
|
35
|
+
writeContract(client, {
|
36
|
+
chain: client.chain ?? null,
|
37
|
+
address: worldDeploy.address,
|
38
|
+
abi: worldAbi,
|
39
|
+
// TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
|
40
|
+
functionName: "registerTable",
|
41
|
+
args: [
|
42
|
+
table.tableId,
|
43
|
+
valueSchemaToFieldLayoutHex(table.valueSchema),
|
44
|
+
keySchemaToHex(table.keySchema),
|
45
|
+
valueSchemaToHex(table.valueSchema),
|
46
|
+
Object.keys(table.keySchema),
|
47
|
+
Object.keys(table.valueSchema),
|
48
|
+
],
|
49
|
+
}),
|
50
|
+
{
|
51
|
+
retries: 3,
|
52
|
+
onFailedAttempt: async (error) => {
|
53
|
+
const delay = error.attemptNumber * 500;
|
54
|
+
debug(`failed to register table ${resourceToLabel(table)}, retrying in ${delay}ms...`);
|
55
|
+
await wait(delay);
|
56
|
+
},
|
57
|
+
},
|
58
|
+
),
|
59
|
+
),
|
60
|
+
);
|
61
|
+
}
|
62
|
+
|
63
|
+
return [];
|
64
|
+
}
|
@@ -0,0 +1,105 @@
|
|
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 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";
|
11
|
+
import { ensureContractsDeployed } from "./ensureContractsDeployed";
|
12
|
+
import { Contract } from "./ensureContract";
|
13
|
+
|
14
|
+
export async function ensureWorldFactory(
|
15
|
+
client: Client<Transport, Chain | undefined, Account>,
|
16
|
+
deployerAddress: Hex,
|
17
|
+
): Promise<Address> {
|
18
|
+
const accessManagementSystemDeployedBytecodeSize = size(accessManagementSystemBuild.deployedBytecode.object as Hex);
|
19
|
+
const accessManagementSystemBytecode = accessManagementSystemBuild.bytecode.object as Hex;
|
20
|
+
const accessManagementSystem = getCreate2Address({
|
21
|
+
from: deployerAddress,
|
22
|
+
bytecode: accessManagementSystemBytecode,
|
23
|
+
salt,
|
24
|
+
});
|
25
|
+
|
26
|
+
const balanceTransferSystemDeployedBytecodeSize = size(balanceTransferSystemBuild.deployedBytecode.object as Hex);
|
27
|
+
const balanceTransferSystemBytecode = balanceTransferSystemBuild.bytecode.object as Hex;
|
28
|
+
const balanceTransferSystem = getCreate2Address({
|
29
|
+
from: deployerAddress,
|
30
|
+
bytecode: balanceTransferSystemBytecode,
|
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
|
98
|
+
await ensureContractsDeployed({
|
99
|
+
client,
|
100
|
+
deployerAddress,
|
101
|
+
contracts: worldFactoryContracts,
|
102
|
+
});
|
103
|
+
|
104
|
+
return worldFactory;
|
105
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import { readFileSync } from "fs";
|
2
|
+
import glob from "glob";
|
3
|
+
import { orderByDependencies } from "./orderByDependencies";
|
4
|
+
import { LinkReferences } from "../utils/findPlaceholders";
|
5
|
+
|
6
|
+
export function findLibraries(forgeOutDir: string): readonly {
|
7
|
+
readonly path: string;
|
8
|
+
readonly name: string;
|
9
|
+
}[] {
|
10
|
+
const artifacts = glob
|
11
|
+
.sync(`${forgeOutDir}/**/*.json`, { ignore: "**/*.abi.json" })
|
12
|
+
.map((path) => JSON.parse(readFileSync(path, "utf8")));
|
13
|
+
|
14
|
+
const libraries = artifacts.flatMap((artifact) => {
|
15
|
+
if (!artifact.metadata) return [];
|
16
|
+
|
17
|
+
const contractPath = Object.keys(artifact.metadata.settings.compilationTarget)[0];
|
18
|
+
const contractName = artifact.metadata.settings.compilationTarget[contractPath];
|
19
|
+
const linkReferences = artifact.bytecode.linkReferences as LinkReferences;
|
20
|
+
|
21
|
+
return Object.entries(linkReferences).flatMap(([libraryPath, reference]) =>
|
22
|
+
Object.keys(reference).map((libraryName) => ({
|
23
|
+
path: libraryPath,
|
24
|
+
name: libraryName,
|
25
|
+
dependentPath: contractPath,
|
26
|
+
dependentName: contractName,
|
27
|
+
})),
|
28
|
+
);
|
29
|
+
});
|
30
|
+
|
31
|
+
return orderByDependencies(
|
32
|
+
libraries,
|
33
|
+
(lib) => `${lib.path}:${lib.name}`,
|
34
|
+
(lib) => [`${lib.dependentPath}:${lib.dependentName}`],
|
35
|
+
);
|
36
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import { Client, toFunctionSelector, parseAbiItem } from "viem";
|
2
|
+
import { WorldDeploy, WorldFunction, worldTables } from "./common";
|
3
|
+
import { debug } from "./debug";
|
4
|
+
import { storeSetRecordEvent } from "@latticexyz/store";
|
5
|
+
import { getLogs } from "viem/actions";
|
6
|
+
import { decodeValueArgs } from "@latticexyz/protocol-parser/internal";
|
7
|
+
import { getTableValue } from "./getTableValue";
|
8
|
+
import { hexToResource } from "@latticexyz/common";
|
9
|
+
|
10
|
+
export async function getFunctions({
|
11
|
+
client,
|
12
|
+
worldDeploy,
|
13
|
+
}: {
|
14
|
+
readonly client: Client;
|
15
|
+
readonly worldDeploy: WorldDeploy;
|
16
|
+
}): Promise<readonly WorldFunction[]> {
|
17
|
+
// This assumes we only use `FunctionSelectors._set(...)`, which is true as of this writing.
|
18
|
+
debug("looking up function signatures for", worldDeploy.address);
|
19
|
+
const logs = await getLogs(client, {
|
20
|
+
strict: true,
|
21
|
+
fromBlock: worldDeploy.deployBlock,
|
22
|
+
toBlock: worldDeploy.stateBlock,
|
23
|
+
address: worldDeploy.address,
|
24
|
+
event: parseAbiItem(storeSetRecordEvent),
|
25
|
+
args: { tableId: worldTables.world_FunctionSignatures.tableId },
|
26
|
+
});
|
27
|
+
|
28
|
+
const signatures = logs.map((log) => {
|
29
|
+
const value = decodeValueArgs(worldTables.world_FunctionSignatures.valueSchema, log.args);
|
30
|
+
return value.functionSignature;
|
31
|
+
});
|
32
|
+
debug("found", signatures.length, "function signatures for", worldDeploy.address);
|
33
|
+
|
34
|
+
// TODO: parallelize with a bulk getRecords
|
35
|
+
const functions = await Promise.all(
|
36
|
+
signatures.map(async (signature) => {
|
37
|
+
const selector = toFunctionSelector(signature);
|
38
|
+
const { systemId, systemFunctionSelector } = await getTableValue({
|
39
|
+
client,
|
40
|
+
worldDeploy,
|
41
|
+
table: worldTables.world_FunctionSelectors,
|
42
|
+
key: { worldFunctionSelector: selector },
|
43
|
+
});
|
44
|
+
const { namespace, name } = hexToResource(systemId);
|
45
|
+
// TODO: find away around undoing contract logic (https://github.com/latticexyz/mud/issues/1708)
|
46
|
+
const systemFunctionSignature = namespace === "" ? signature : signature.replace(`${namespace}_${name}_`, "");
|
47
|
+
return {
|
48
|
+
signature,
|
49
|
+
selector,
|
50
|
+
systemId,
|
51
|
+
systemFunctionSignature,
|
52
|
+
systemFunctionSelector,
|
53
|
+
};
|
54
|
+
}),
|
55
|
+
);
|
56
|
+
|
57
|
+
return functions;
|
58
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import { Client, parseAbiItem, Hex, Address, getAddress } from "viem";
|
2
|
+
import { WorldDeploy, worldTables } from "./common";
|
3
|
+
import { debug } from "./debug";
|
4
|
+
import { storeSpliceStaticDataEvent } from "@latticexyz/store";
|
5
|
+
import { getLogs } from "viem/actions";
|
6
|
+
import { decodeKey } from "@latticexyz/protocol-parser/internal";
|
7
|
+
import { getTableValue } from "./getTableValue";
|
8
|
+
|
9
|
+
export async function getResourceAccess({
|
10
|
+
client,
|
11
|
+
worldDeploy,
|
12
|
+
}: {
|
13
|
+
readonly client: Client;
|
14
|
+
readonly worldDeploy: WorldDeploy;
|
15
|
+
}): Promise<readonly { readonly resourceId: Hex; readonly address: Address }[]> {
|
16
|
+
// This assumes we only use `ResourceAccess._set(...)`, which is true as of this writing.
|
17
|
+
// 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
|
18
|
+
|
19
|
+
debug("looking up resource access for", worldDeploy.address);
|
20
|
+
|
21
|
+
const logs = await getLogs(client, {
|
22
|
+
strict: true,
|
23
|
+
fromBlock: worldDeploy.deployBlock,
|
24
|
+
toBlock: worldDeploy.stateBlock,
|
25
|
+
address: worldDeploy.address,
|
26
|
+
// our usage of `ResourceAccess._set(...)` emits a splice instead of set record
|
27
|
+
// TODO: https://github.com/latticexyz/mud/issues/479
|
28
|
+
event: parseAbiItem(storeSpliceStaticDataEvent),
|
29
|
+
args: { tableId: worldTables.world_ResourceAccess.tableId },
|
30
|
+
});
|
31
|
+
|
32
|
+
const keys = logs.map((log) => decodeKey(worldTables.world_ResourceAccess.keySchema, log.args.keyTuple));
|
33
|
+
|
34
|
+
const access = (
|
35
|
+
await Promise.all(
|
36
|
+
keys.map(
|
37
|
+
async (key) =>
|
38
|
+
[key, await getTableValue({ client, worldDeploy, table: worldTables.world_ResourceAccess, key })] as const,
|
39
|
+
),
|
40
|
+
)
|
41
|
+
)
|
42
|
+
.filter(([, value]) => value.access)
|
43
|
+
.map(([key]) => ({
|
44
|
+
resourceId: key.resourceId,
|
45
|
+
address: getAddress(key.caller),
|
46
|
+
}));
|
47
|
+
|
48
|
+
debug("found", access.length, "resource<>address access pairs");
|
49
|
+
|
50
|
+
return access;
|
51
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { Client, parseAbiItem, Hex } from "viem";
|
2
|
+
import { getLogs } from "viem/actions";
|
3
|
+
import { storeSpliceStaticDataEvent } from "@latticexyz/store";
|
4
|
+
import { WorldDeploy, storeTables } from "./common";
|
5
|
+
import { debug } from "./debug";
|
6
|
+
|
7
|
+
export async function getResourceIds({
|
8
|
+
client,
|
9
|
+
worldDeploy,
|
10
|
+
}: {
|
11
|
+
readonly client: Client;
|
12
|
+
readonly worldDeploy: WorldDeploy;
|
13
|
+
}): Promise<readonly Hex[]> {
|
14
|
+
// This assumes we only use `ResourceIds._setExists(true)`, which is true as of this writing.
|
15
|
+
// 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
|
+
debug("looking up resource IDs for", worldDeploy.address);
|
18
|
+
const logs = await getLogs(client, {
|
19
|
+
strict: true,
|
20
|
+
address: worldDeploy.address,
|
21
|
+
fromBlock: worldDeploy.deployBlock,
|
22
|
+
toBlock: worldDeploy.stateBlock,
|
23
|
+
event: parseAbiItem(storeSpliceStaticDataEvent),
|
24
|
+
args: { tableId: storeTables.store_ResourceIds.tableId },
|
25
|
+
});
|
26
|
+
|
27
|
+
const resourceIds = logs.map((log) => log.args.keyTuple[0]);
|
28
|
+
debug("found", resourceIds.length, "resource IDs for", worldDeploy.address);
|
29
|
+
|
30
|
+
return resourceIds;
|
31
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { DeployedSystem, WorldDeploy, worldTables } from "./common";
|
2
|
+
import { Client } from "viem";
|
3
|
+
import { getResourceIds } from "./getResourceIds";
|
4
|
+
import { hexToResource, resourceToLabel } from "@latticexyz/common";
|
5
|
+
import { getTableValue } from "./getTableValue";
|
6
|
+
import { debug } from "./debug";
|
7
|
+
import { getFunctions } from "./getFunctions";
|
8
|
+
import { getResourceAccess } from "./getResourceAccess";
|
9
|
+
|
10
|
+
export async function getSystems({
|
11
|
+
client,
|
12
|
+
worldDeploy,
|
13
|
+
}: {
|
14
|
+
readonly client: Client;
|
15
|
+
readonly worldDeploy: WorldDeploy;
|
16
|
+
}): Promise<readonly DeployedSystem[]> {
|
17
|
+
const [resourceIds, functions, resourceAccess] = await Promise.all([
|
18
|
+
getResourceIds({ client, worldDeploy }),
|
19
|
+
getFunctions({ client, worldDeploy }),
|
20
|
+
getResourceAccess({ client, worldDeploy }),
|
21
|
+
]);
|
22
|
+
const systems = resourceIds.map(hexToResource).filter((resource) => resource.type === "system");
|
23
|
+
|
24
|
+
debug("looking up systems", systems.map(resourceToLabel).join(", "));
|
25
|
+
return await Promise.all(
|
26
|
+
systems.map(async (system): Promise<DeployedSystem> => {
|
27
|
+
const { system: address, publicAccess } = await getTableValue({
|
28
|
+
client,
|
29
|
+
worldDeploy,
|
30
|
+
table: worldTables.world_Systems,
|
31
|
+
key: { systemId: system.resourceId },
|
32
|
+
});
|
33
|
+
const systemFunctions = functions.filter((func) => func.systemId === system.resourceId);
|
34
|
+
return {
|
35
|
+
address,
|
36
|
+
namespace: system.namespace,
|
37
|
+
name: system.name,
|
38
|
+
systemId: system.resourceId,
|
39
|
+
allowAll: publicAccess,
|
40
|
+
allowedAddresses: resourceAccess
|
41
|
+
.filter(({ resourceId }) => resourceId === system.resourceId)
|
42
|
+
.map(({ address }) => address),
|
43
|
+
functions: systemFunctions,
|
44
|
+
};
|
45
|
+
}),
|
46
|
+
);
|
47
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { SchemaToPrimitives, decodeValueArgs, encodeKey } from "@latticexyz/protocol-parser/internal";
|
2
|
+
import { WorldDeploy, worldAbi } from "./common";
|
3
|
+
import { Client } from "viem";
|
4
|
+
import { readContract } from "viem/actions";
|
5
|
+
import { Table } from "./configToTables";
|
6
|
+
|
7
|
+
export async function getTableValue<table extends Table>({
|
8
|
+
client,
|
9
|
+
worldDeploy,
|
10
|
+
table,
|
11
|
+
key,
|
12
|
+
}: {
|
13
|
+
readonly client: Client;
|
14
|
+
readonly worldDeploy: WorldDeploy;
|
15
|
+
readonly table: table;
|
16
|
+
readonly key: SchemaToPrimitives<table["keySchema"]>;
|
17
|
+
}): Promise<SchemaToPrimitives<table["valueSchema"]>> {
|
18
|
+
const [staticData, encodedLengths, dynamicData] = await readContract(client, {
|
19
|
+
blockNumber: worldDeploy.stateBlock,
|
20
|
+
address: worldDeploy.address,
|
21
|
+
abi: worldAbi,
|
22
|
+
functionName: "getRecord",
|
23
|
+
args: [table.tableId, encodeKey(table.keySchema, key)],
|
24
|
+
});
|
25
|
+
return decodeValueArgs(table.valueSchema, {
|
26
|
+
staticData,
|
27
|
+
encodedLengths,
|
28
|
+
dynamicData,
|
29
|
+
});
|
30
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import { Client, parseAbiItem, decodeAbiParameters, parseAbiParameters } from "viem";
|
2
|
+
import { Table } from "./configToTables";
|
3
|
+
import { hexToResource } from "@latticexyz/common";
|
4
|
+
import { WorldDeploy, storeTables } from "./common";
|
5
|
+
import { debug } from "./debug";
|
6
|
+
import { storeSetRecordEvent } from "@latticexyz/store";
|
7
|
+
import { getLogs } from "viem/actions";
|
8
|
+
import { KeySchema, ValueSchema, decodeKey, decodeValueArgs, hexToSchema } from "@latticexyz/protocol-parser/internal";
|
9
|
+
|
10
|
+
export async function getTables({
|
11
|
+
client,
|
12
|
+
worldDeploy,
|
13
|
+
}: {
|
14
|
+
readonly client: Client;
|
15
|
+
readonly worldDeploy: WorldDeploy;
|
16
|
+
}): Promise<readonly Table[]> {
|
17
|
+
// This assumes we only use `Tables._set(...)`, which is true as of this writing.
|
18
|
+
// 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
|
19
|
+
// TODO: consider moving this to a batched getRecord for Tables table
|
20
|
+
|
21
|
+
debug("looking up tables for", worldDeploy.address);
|
22
|
+
const logs = await getLogs(client, {
|
23
|
+
strict: true,
|
24
|
+
// this may fail for certain RPC providers with block range limits
|
25
|
+
// if so, could potentially use our fetchLogs helper (which does pagination)
|
26
|
+
fromBlock: worldDeploy.deployBlock,
|
27
|
+
toBlock: worldDeploy.stateBlock,
|
28
|
+
address: worldDeploy.address,
|
29
|
+
event: parseAbiItem(storeSetRecordEvent),
|
30
|
+
args: { tableId: storeTables.store_Tables.tableId },
|
31
|
+
});
|
32
|
+
|
33
|
+
// TODO: combine with store-sync logToTable and export from somewhere
|
34
|
+
const tables = logs.map((log) => {
|
35
|
+
const { tableId } = decodeKey(storeTables.store_Tables.keySchema, log.args.keyTuple);
|
36
|
+
const { namespace, name } = hexToResource(tableId);
|
37
|
+
const value = decodeValueArgs(storeTables.store_Tables.valueSchema, log.args);
|
38
|
+
|
39
|
+
// TODO: migrate to better helper
|
40
|
+
const keySchemaFields = hexToSchema(value.keySchema);
|
41
|
+
const valueSchemaFields = hexToSchema(value.valueSchema);
|
42
|
+
const keyNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedKeyNames)[0];
|
43
|
+
const fieldNames = decodeAbiParameters(parseAbiParameters("string[]"), value.abiEncodedFieldNames)[0];
|
44
|
+
|
45
|
+
const valueAbiTypes = [...valueSchemaFields.staticFields, ...valueSchemaFields.dynamicFields];
|
46
|
+
|
47
|
+
const keySchema = Object.fromEntries(
|
48
|
+
keySchemaFields.staticFields.map((abiType, i) => [keyNames[i], abiType]),
|
49
|
+
) as KeySchema;
|
50
|
+
const valueSchema = Object.fromEntries(valueAbiTypes.map((abiType, i) => [fieldNames[i], abiType])) as ValueSchema;
|
51
|
+
|
52
|
+
return { namespace, name, tableId, keySchema, valueSchema } as const;
|
53
|
+
});
|
54
|
+
// TODO: filter/detect duplicates?
|
55
|
+
|
56
|
+
debug("found", tables.length, "tables for", worldDeploy.address);
|
57
|
+
|
58
|
+
return tables;
|
59
|
+
}
|