@latticexyz/cli 2.0.0-skystrife-playtest-9e9511d4 → 2.0.0-transaction-context-324984c5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/chunk-22IIKR4S.js +4 -0
  2. package/dist/chunk-22IIKR4S.js.map +1 -0
  3. package/dist/commands-3JV3U43E.js +27 -0
  4. package/dist/commands-3JV3U43E.js.map +1 -0
  5. package/dist/errors-XGN6V2Y3.js +2 -0
  6. package/dist/errors-XGN6V2Y3.js.map +1 -0
  7. package/dist/index.js +0 -1
  8. package/dist/mud.js +1 -14
  9. package/dist/mud.js.map +1 -1
  10. package/package.json +20 -13
  11. package/src/build.ts +44 -0
  12. package/src/commands/build.ts +36 -0
  13. package/src/commands/deploy.ts +7 -30
  14. package/src/commands/dev-contracts.ts +76 -128
  15. package/src/commands/index.ts +2 -0
  16. package/src/commands/set-version.ts +23 -61
  17. package/src/commands/tablegen.ts +3 -2
  18. package/src/commands/test.ts +30 -36
  19. package/src/commands/trace.ts +13 -6
  20. package/src/commands/worldgen.ts +1 -1
  21. package/src/common.ts +1 -0
  22. package/src/debug.ts +10 -0
  23. package/src/deploy/common.ts +76 -0
  24. package/src/deploy/configToTables.ts +68 -0
  25. package/src/deploy/create2/README.md +9 -0
  26. package/src/deploy/create2/deployment.json +7 -0
  27. package/src/deploy/debug.ts +10 -0
  28. package/src/deploy/deploy.ts +116 -0
  29. package/src/deploy/deployWorld.ts +37 -0
  30. package/src/deploy/ensureContract.ts +61 -0
  31. package/src/deploy/ensureContractsDeployed.ts +25 -0
  32. package/src/deploy/ensureDeployer.ts +36 -0
  33. package/src/deploy/ensureFunctions.ts +86 -0
  34. package/src/deploy/ensureModules.ts +73 -0
  35. package/src/deploy/ensureNamespaceOwner.ts +71 -0
  36. package/src/deploy/ensureSystems.ts +162 -0
  37. package/src/deploy/ensureTables.ts +65 -0
  38. package/src/deploy/ensureWorldFactory.ts +118 -0
  39. package/src/deploy/getFunctions.ts +58 -0
  40. package/src/deploy/getResourceAccess.ts +51 -0
  41. package/src/deploy/getResourceIds.ts +31 -0
  42. package/src/deploy/getSystems.ts +48 -0
  43. package/src/deploy/getTableValue.ts +30 -0
  44. package/src/deploy/getTables.ts +59 -0
  45. package/src/deploy/getWorldDeploy.ts +39 -0
  46. package/src/deploy/logsToWorldDeploy.ts +49 -0
  47. package/src/deploy/resolveConfig.ts +151 -0
  48. package/src/deploy/resourceLabel.ts +3 -0
  49. package/src/index.ts +1 -1
  50. package/src/mud.ts +37 -31
  51. package/src/mudPackages.ts +24 -0
  52. package/src/runDeploy.ts +131 -0
  53. package/src/utils/modules/constants.ts +26 -0
  54. package/src/utils/utils/getContractData.ts +32 -0
  55. package/src/utils/utils/postDeploy.ts +25 -0
  56. package/dist/chunk-OJAPOMSC.js +0 -11
  57. package/dist/chunk-OJAPOMSC.js.map +0 -1
  58. package/src/utils/deploy.ts +0 -620
  59. package/src/utils/deployHandler.ts +0 -93
  60. package/src/utils/getChainId.ts +0 -10
  61. package/src/utils/index.ts +0 -6
@@ -0,0 +1,71 @@
1
+ import { Account, Chain, Client, Hex, Transport, getAddress } from "viem";
2
+ import { WorldDeploy, worldAbi, worldTables } from "./common";
3
+ import { hexToResource, resourceToHex, writeContract } from "@latticexyz/common";
4
+ import { getResourceIds } from "./getResourceIds";
5
+ import { getTableValue } from "./getTableValue";
6
+ import { debug } from "./debug";
7
+
8
+ export async function ensureNamespaceOwner({
9
+ client,
10
+ worldDeploy,
11
+ resourceIds,
12
+ }: {
13
+ readonly client: Client<Transport, Chain | undefined, Account>;
14
+ readonly worldDeploy: WorldDeploy;
15
+ readonly resourceIds: readonly Hex[];
16
+ }): Promise<readonly Hex[]> {
17
+ const desiredNamespaces = Array.from(new Set(resourceIds.map((resourceId) => hexToResource(resourceId).namespace)));
18
+ const existingResourceIds = await getResourceIds({ client, worldDeploy });
19
+ const existingNamespaces = new Set(existingResourceIds.map((resourceId) => hexToResource(resourceId).namespace));
20
+ if (existingNamespaces.size) {
21
+ debug(
22
+ "found",
23
+ existingNamespaces.size,
24
+ "existing namespaces:",
25
+ Array.from(existingNamespaces)
26
+ .map((namespace) => (namespace === "" ? "<root>" : namespace))
27
+ .join(", ")
28
+ );
29
+ }
30
+
31
+ // Assert ownership of existing namespaces
32
+ const existingDesiredNamespaces = desiredNamespaces.filter((namespace) => existingNamespaces.has(namespace));
33
+ const namespaceOwners = await Promise.all(
34
+ existingDesiredNamespaces.map(async (namespace) => {
35
+ const { owner } = await getTableValue({
36
+ client,
37
+ worldDeploy,
38
+ table: worldTables.world_NamespaceOwner,
39
+ key: { namespaceId: resourceToHex({ type: "namespace", namespace, name: "" }) },
40
+ });
41
+ return [namespace, owner];
42
+ })
43
+ );
44
+
45
+ const unauthorizedNamespaces = namespaceOwners
46
+ .filter(([, owner]) => getAddress(owner) !== getAddress(client.account.address))
47
+ .map(([namespace]) => namespace);
48
+
49
+ if (unauthorizedNamespaces.length) {
50
+ throw new Error(`You are attempting to deploy to namespaces you do not own: ${unauthorizedNamespaces.join(", ")}`);
51
+ }
52
+
53
+ // Register missing namespaces
54
+ const missingNamespaces = desiredNamespaces.filter((namespace) => !existingNamespaces.has(namespace));
55
+ if (missingNamespaces.length > 0) {
56
+ debug("registering namespaces", Array.from(missingNamespaces).join(", "));
57
+ }
58
+ const registrationTxs = Promise.all(
59
+ missingNamespaces.map((namespace) =>
60
+ writeContract(client, {
61
+ chain: client.chain ?? null,
62
+ address: worldDeploy.address,
63
+ abi: worldAbi,
64
+ functionName: "registerNamespace",
65
+ args: [resourceToHex({ namespace, type: "namespace", name: "" })],
66
+ })
67
+ )
68
+ );
69
+
70
+ return registrationTxs;
71
+ }
@@ -0,0 +1,162 @@
1
+ import { Client, Transport, Chain, Account, Hex, getAddress } from "viem";
2
+ import { writeContract } from "@latticexyz/common";
3
+ import { System, WorldDeploy, worldAbi } from "./common";
4
+ import { debug } from "./debug";
5
+ import { resourceLabel } from "./resourceLabel";
6
+ import { getSystems } from "./getSystems";
7
+ import { getResourceAccess } from "./getResourceAccess";
8
+ import { uniqueBy, wait } from "@latticexyz/common/utils";
9
+ import pRetry from "p-retry";
10
+ import { ensureContractsDeployed } from "./ensureContractsDeployed";
11
+
12
+ export async function ensureSystems({
13
+ client,
14
+ worldDeploy,
15
+ systems,
16
+ }: {
17
+ readonly client: Client<Transport, Chain | undefined, Account>;
18
+ readonly worldDeploy: WorldDeploy;
19
+ readonly systems: readonly System[];
20
+ }): Promise<readonly Hex[]> {
21
+ const [worldSystems, worldAccess] = await Promise.all([
22
+ getSystems({ client, worldDeploy }),
23
+ getResourceAccess({ client, worldDeploy }),
24
+ ]);
25
+ const systemIds = systems.map((system) => system.systemId);
26
+ const currentAccess = worldAccess.filter(({ resourceId }) => systemIds.includes(resourceId));
27
+ const desiredAccess = systems.flatMap((system) =>
28
+ system.allowedAddresses.map((address) => ({ resourceId: system.systemId, address }))
29
+ );
30
+
31
+ const accessToAdd = desiredAccess.filter(
32
+ (access) =>
33
+ !currentAccess.some(
34
+ ({ resourceId, address }) =>
35
+ resourceId === access.resourceId && getAddress(address) === getAddress(access.address)
36
+ )
37
+ );
38
+
39
+ const accessToRemove = currentAccess.filter(
40
+ (access) =>
41
+ !desiredAccess.some(
42
+ ({ resourceId, address }) =>
43
+ resourceId === access.resourceId && getAddress(address) === getAddress(access.address)
44
+ )
45
+ );
46
+
47
+ // TODO: move each system access+registration to batch call to be atomic
48
+
49
+ if (accessToRemove.length) {
50
+ debug("revoking", accessToRemove.length, "access grants");
51
+ }
52
+ if (accessToAdd.length) {
53
+ debug("adding", accessToAdd.length, "access grants");
54
+ }
55
+
56
+ const accessTxs = [
57
+ ...accessToRemove.map((access) =>
58
+ pRetry(
59
+ () =>
60
+ writeContract(client, {
61
+ chain: client.chain ?? null,
62
+ address: worldDeploy.address,
63
+ abi: worldAbi,
64
+ functionName: "revokeAccess",
65
+ args: [access.resourceId, access.address],
66
+ }),
67
+ {
68
+ retries: 3,
69
+ onFailedAttempt: async (error) => {
70
+ const delay = error.attemptNumber * 500;
71
+ debug(`failed to revoke access, retrying in ${delay}ms...`);
72
+ await wait(delay);
73
+ },
74
+ }
75
+ )
76
+ ),
77
+ ...accessToAdd.map((access) =>
78
+ pRetry(
79
+ () =>
80
+ writeContract(client, {
81
+ chain: client.chain ?? null,
82
+ address: worldDeploy.address,
83
+ abi: worldAbi,
84
+ functionName: "grantAccess",
85
+ args: [access.resourceId, access.address],
86
+ }),
87
+ {
88
+ retries: 3,
89
+ onFailedAttempt: async (error) => {
90
+ const delay = error.attemptNumber * 500;
91
+ debug(`failed to grant access, retrying in ${delay}ms...`);
92
+ await wait(delay);
93
+ },
94
+ }
95
+ )
96
+ ),
97
+ ];
98
+
99
+ const existingSystems = systems.filter((system) =>
100
+ worldSystems.some(
101
+ (worldSystem) =>
102
+ worldSystem.systemId === system.systemId && getAddress(worldSystem.address) === getAddress(system.address)
103
+ )
104
+ );
105
+ if (existingSystems.length) {
106
+ debug("existing systems", existingSystems.map(resourceLabel).join(", "));
107
+ }
108
+ const existingSystemIds = existingSystems.map((system) => system.systemId);
109
+
110
+ const missingSystems = systems.filter((system) => !existingSystemIds.includes(system.systemId));
111
+ if (!missingSystems.length) return [];
112
+
113
+ const systemsToUpgrade = missingSystems.filter((system) =>
114
+ worldSystems.some(
115
+ (worldSystem) =>
116
+ worldSystem.systemId === system.systemId && getAddress(worldSystem.address) !== getAddress(system.address)
117
+ )
118
+ );
119
+ if (systemsToUpgrade.length) {
120
+ debug("upgrading systems", systemsToUpgrade.map(resourceLabel).join(", "));
121
+ }
122
+
123
+ const systemsToAdd = missingSystems.filter(
124
+ (system) => !worldSystems.some((worldSystem) => worldSystem.systemId === system.systemId)
125
+ );
126
+ if (systemsToAdd.length) {
127
+ debug("registering new systems", systemsToAdd.map(resourceLabel).join(", "));
128
+ }
129
+
130
+ await ensureContractsDeployed({
131
+ client,
132
+ contracts: uniqueBy(missingSystems, (system) => getAddress(system.address)).map((system) => ({
133
+ bytecode: system.bytecode,
134
+ deployedBytecodeSize: system.deployedBytecodeSize,
135
+ label: `${resourceLabel(system)} system`,
136
+ })),
137
+ });
138
+
139
+ const registerTxs = missingSystems.map((system) =>
140
+ pRetry(
141
+ () =>
142
+ writeContract(client, {
143
+ chain: client.chain ?? null,
144
+ address: worldDeploy.address,
145
+ abi: worldAbi,
146
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
147
+ functionName: "registerSystem",
148
+ args: [system.systemId, system.address, system.allowAll],
149
+ }),
150
+ {
151
+ retries: 3,
152
+ onFailedAttempt: async (error) => {
153
+ const delay = error.attemptNumber * 500;
154
+ debug(`failed to register system ${resourceLabel(system)}, retrying in ${delay}ms...`);
155
+ await wait(delay);
156
+ },
157
+ }
158
+ )
159
+ );
160
+
161
+ return await Promise.all([...accessTxs, ...registerTxs]);
162
+ }
@@ -0,0 +1,65 @@
1
+ import { Client, Transport, Chain, Account, Hex } from "viem";
2
+ import { Table } from "./configToTables";
3
+ import { writeContract } from "@latticexyz/common";
4
+ import { WorldDeploy, worldAbi } from "./common";
5
+ import { valueSchemaToFieldLayoutHex, keySchemaToHex, valueSchemaToHex } from "@latticexyz/protocol-parser";
6
+ import { debug } from "./debug";
7
+ import { resourceLabel } from "./resourceLabel";
8
+ import { getTables } from "./getTables";
9
+ import pRetry from "p-retry";
10
+ import { wait } from "@latticexyz/common/utils";
11
+
12
+ export async function ensureTables({
13
+ client,
14
+ worldDeploy,
15
+ tables,
16
+ }: {
17
+ readonly client: Client<Transport, Chain | undefined, Account>;
18
+ readonly worldDeploy: WorldDeploy;
19
+ readonly tables: readonly Table[];
20
+ }): Promise<readonly Hex[]> {
21
+ const worldTables = await getTables({ client, worldDeploy });
22
+ const worldTableIds = worldTables.map((table) => table.tableId);
23
+
24
+ const existingTables = tables.filter((table) => worldTableIds.includes(table.tableId));
25
+ if (existingTables.length) {
26
+ debug("existing tables", existingTables.map(resourceLabel).join(", "));
27
+ }
28
+
29
+ const missingTables = tables.filter((table) => !worldTableIds.includes(table.tableId));
30
+ if (missingTables.length) {
31
+ debug("registering tables", missingTables.map(resourceLabel).join(", "));
32
+ return await Promise.all(
33
+ missingTables.map((table) =>
34
+ pRetry(
35
+ () =>
36
+ writeContract(client, {
37
+ chain: client.chain ?? null,
38
+ address: worldDeploy.address,
39
+ abi: worldAbi,
40
+ // TODO: replace with batchCall (https://github.com/latticexyz/mud/issues/1645)
41
+ functionName: "registerTable",
42
+ args: [
43
+ table.tableId,
44
+ valueSchemaToFieldLayoutHex(table.valueSchema),
45
+ keySchemaToHex(table.keySchema),
46
+ valueSchemaToHex(table.valueSchema),
47
+ Object.keys(table.keySchema),
48
+ Object.keys(table.valueSchema),
49
+ ],
50
+ }),
51
+ {
52
+ retries: 3,
53
+ onFailedAttempt: async (error) => {
54
+ const delay = error.attemptNumber * 500;
55
+ debug(`failed to register table ${resourceLabel(table)}, retrying in ${delay}ms...`);
56
+ await wait(delay);
57
+ },
58
+ }
59
+ )
60
+ )
61
+ );
62
+ }
63
+
64
+ return [];
65
+ }
@@ -0,0 +1,118 @@
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 } from "viem";
10
+ import { deployer } from "./ensureDeployer";
11
+ import { salt } from "./common";
12
+ import { ensureContractsDeployed } from "./ensureContractsDeployed";
13
+ import { Contract } from "./ensureContract";
14
+
15
+ export const accessManagementSystemDeployedBytecodeSize = size(
16
+ accessManagementSystemBuild.deployedBytecode.object as Hex
17
+ );
18
+ export const accessManagementSystemBytecode = encodeDeployData({
19
+ bytecode: accessManagementSystemBuild.bytecode.object as Hex,
20
+ abi: [],
21
+ });
22
+ export const accessManagementSystem = getCreate2Address({
23
+ from: deployer,
24
+ bytecode: accessManagementSystemBytecode,
25
+ salt,
26
+ });
27
+
28
+ export const balanceTransferSystemDeployedBytecodeSize = size(
29
+ balanceTransferSystemBuild.deployedBytecode.object as Hex
30
+ );
31
+ export const balanceTransferSystemBytecode = encodeDeployData({
32
+ bytecode: balanceTransferSystemBuild.bytecode.object as Hex,
33
+ abi: [],
34
+ });
35
+ export const balanceTransferSystem = getCreate2Address({
36
+ from: deployer,
37
+ bytecode: balanceTransferSystemBytecode,
38
+ salt,
39
+ });
40
+
41
+ export const batchCallSystemDeployedBytecodeSize = size(batchCallSystemBuild.deployedBytecode.object as Hex);
42
+ export const batchCallSystemBytecode = encodeDeployData({
43
+ bytecode: batchCallSystemBuild.bytecode.object as Hex,
44
+ abi: [],
45
+ });
46
+ export const batchCallSystem = getCreate2Address({ from: deployer, bytecode: batchCallSystemBytecode, salt });
47
+
48
+ export const registrationDeployedBytecodeSize = size(registrationSystemBuild.deployedBytecode.object as Hex);
49
+ export const registrationBytecode = encodeDeployData({
50
+ bytecode: registrationSystemBuild.bytecode.object as Hex,
51
+ abi: [],
52
+ });
53
+ export const registration = getCreate2Address({
54
+ from: deployer,
55
+ bytecode: registrationBytecode,
56
+ salt,
57
+ });
58
+
59
+ export const initModuleDeployedBytecodeSize = size(initModuleBuild.deployedBytecode.object as Hex);
60
+ export const initModuleBytecode = encodeDeployData({
61
+ bytecode: initModuleBuild.bytecode.object as Hex,
62
+ abi: initModuleAbi,
63
+ args: [accessManagementSystem, balanceTransferSystem, batchCallSystem, registration],
64
+ });
65
+
66
+ export const initModule = getCreate2Address({ from: deployer, bytecode: initModuleBytecode, salt });
67
+
68
+ export const worldFactoryDeployedBytecodeSize = size(worldFactoryBuild.deployedBytecode.object as Hex);
69
+ export const worldFactoryBytecode = encodeDeployData({
70
+ bytecode: worldFactoryBuild.bytecode.object as Hex,
71
+ abi: worldFactoryAbi,
72
+ args: [initModule],
73
+ });
74
+
75
+ export const worldFactory = getCreate2Address({ from: deployer, bytecode: worldFactoryBytecode, salt });
76
+
77
+ export const worldFactoryContracts: readonly Contract[] = [
78
+ {
79
+ bytecode: accessManagementSystemBytecode,
80
+ deployedBytecodeSize: accessManagementSystemDeployedBytecodeSize,
81
+ label: "access management system",
82
+ },
83
+ {
84
+ bytecode: balanceTransferSystemBytecode,
85
+ deployedBytecodeSize: balanceTransferSystemDeployedBytecodeSize,
86
+ label: "balance transfer system",
87
+ },
88
+ {
89
+ bytecode: batchCallSystemBytecode,
90
+ deployedBytecodeSize: batchCallSystemDeployedBytecodeSize,
91
+ label: "batch call system",
92
+ },
93
+ {
94
+ bytecode: registrationBytecode,
95
+ deployedBytecodeSize: registrationDeployedBytecodeSize,
96
+ label: "core registration system",
97
+ },
98
+ {
99
+ bytecode: initModuleBytecode,
100
+ deployedBytecodeSize: initModuleDeployedBytecodeSize,
101
+ label: "core module",
102
+ },
103
+ {
104
+ bytecode: worldFactoryBytecode,
105
+ deployedBytecodeSize: worldFactoryDeployedBytecodeSize,
106
+ label: "world factory",
107
+ },
108
+ ];
109
+
110
+ export async function ensureWorldFactory(
111
+ client: Client<Transport, Chain | undefined, Account>
112
+ ): Promise<readonly Hex[]> {
113
+ // WorldFactory constructor doesn't call InitModule, only sets its address, so we can do these in parallel since the address is deterministic
114
+ return await ensureContractsDeployed({
115
+ client,
116
+ contracts: worldFactoryContracts,
117
+ });
118
+ }
@@ -0,0 +1,58 @@
1
+ import { Client, getFunctionSelector, 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";
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 = getFunctionSelector(signature);
38
+ const { systemId, systemFunctionSelector } = await getTableValue({
39
+ client,
40
+ worldDeploy,
41
+ table: worldTables.world_FunctionSelectors,
42
+ key: { functionSelector: 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";
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,48 @@
1
+ import { System, WorldDeploy, worldTables } from "./common";
2
+ import { Client } from "viem";
3
+ import { getResourceIds } from "./getResourceIds";
4
+ import { hexToResource } from "@latticexyz/common";
5
+ import { getTableValue } from "./getTableValue";
6
+ import { debug } from "./debug";
7
+ import { resourceLabel } from "./resourceLabel";
8
+ import { getFunctions } from "./getFunctions";
9
+ import { getResourceAccess } from "./getResourceAccess";
10
+
11
+ export async function getSystems({
12
+ client,
13
+ worldDeploy,
14
+ }: {
15
+ readonly client: Client;
16
+ readonly worldDeploy: WorldDeploy;
17
+ }): Promise<readonly Omit<System, "abi" | "bytecode" | "deployedBytecodeSize">[]> {
18
+ const [resourceIds, functions, resourceAccess] = await Promise.all([
19
+ getResourceIds({ client, worldDeploy }),
20
+ getFunctions({ client, worldDeploy }),
21
+ getResourceAccess({ client, worldDeploy }),
22
+ ]);
23
+ const systems = resourceIds.map(hexToResource).filter((resource) => resource.type === "system");
24
+
25
+ debug("looking up systems", systems.map(resourceLabel).join(", "));
26
+ return await Promise.all(
27
+ systems.map(async (system) => {
28
+ const { system: address, publicAccess } = await getTableValue({
29
+ client,
30
+ worldDeploy,
31
+ table: worldTables.world_Systems,
32
+ key: { systemId: system.resourceId },
33
+ });
34
+ const systemFunctions = functions.filter((func) => func.systemId === system.resourceId);
35
+ return {
36
+ address,
37
+ namespace: system.namespace,
38
+ name: system.name,
39
+ systemId: system.resourceId,
40
+ allowAll: publicAccess,
41
+ allowedAddresses: resourceAccess
42
+ .filter(({ resourceId }) => resourceId === system.resourceId)
43
+ .map(({ address }) => address),
44
+ functions: systemFunctions,
45
+ };
46
+ })
47
+ );
48
+ }
@@ -0,0 +1,30 @@
1
+ import { SchemaToPrimitives, decodeValueArgs, encodeKey } from "@latticexyz/protocol-parser";
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
+ }