Skip to content

Commit

Permalink
feat(world,cli): add system deploy config (#3011)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored Aug 6, 2024
1 parent af4ae94 commit 86a8104
Show file tree
Hide file tree
Showing 15 changed files with 154 additions and 53 deletions.
22 changes: 22 additions & 0 deletions .changeset/quiet-pugs-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@latticexyz/world": patch
---

Added `deploy` config options to systems in the MUD config:

- `disabled` to toggle deploying the system (defaults to `false`)
- `registerWorldFunctions` to toggle registering namespace-prefixed system functions on the world (defaults to `true`)

```ts
import { defineWorld } from "@latticexyz/world";

export default defineWorld({
systems: {
HiddenSystem: {
deploy: {
registerWorldFunctions: false,
},
},
},
});
```
9 changes: 9 additions & 0 deletions docs/pages/config/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ The MUD config has two modes: single namespace and multiple namespaces. By defau
</Param>
<Param name="openAccess">Whether or not any address can call this system. Defaults to `true`.</Param>
<Param name="accessList">A list of contract addresses or system labels that can call this system, used with `openAccess: false`.</Param>
<Param name="deploy">
Customize how to deploy this system.

<Params>
<Param name="disabled">Disable deployment of this system. Defaults to `false`.</Param>
<Param name="registerWorldFunctions">Whether this system's functions should be registered on the world, prefixed with the system namespace. Defaults to `true`.</Param>
</Params>

</Param>
</Params>

</Param>
Expand Down
7 changes: 7 additions & 0 deletions e2e/packages/contracts/mud.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export default defineWorld({
key: [],
},
},
systems: {
HiddenSystem: {
deploy: {
registerWorldFunctions: false,
},
},
},
modules: [
{
artifactPath:
Expand Down
10 changes: 10 additions & 0 deletions e2e/packages/contracts/src/systems/HiddenSystem.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;

import { System } from "@latticexyz/world/src/System.sol";

contract HiddenSystem is System {
function hidden() public {
// this system is only callable with system ID, not via world
}
}
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export type System = DeterministicContract & {
readonly allowAll: boolean;
readonly allowedAddresses: readonly Hex[];
readonly allowedSystemIds: readonly Hex[];
readonly functions: readonly WorldFunction[];
readonly worldFunctions: readonly WorldFunction[];
};

export type DeployedSystem = Omit<System, "abi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds"> & {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export async function deploy({
const functionTxs = await ensureFunctions({
client,
worldDeploy,
functions: systems.flatMap((system) => system.functions),
functions: systems.flatMap((system) => system.worldFunctions),
});
const moduleTxs = await ensureModules({
client,
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/deploy/getSystems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function getSystems({
table: worldConfig.namespaces.world.tables.Systems,
key: { systemId: system.resourceId },
});
const systemFunctions = functions.filter((func) => func.systemId === system.resourceId);
const worldFunctions = functions.filter((func) => func.systemId === system.resourceId);
return {
address,
namespace: system.namespace,
Expand All @@ -41,7 +41,7 @@ export async function getSystems({
allowedAddresses: resourceAccess
.filter(({ resourceId }) => resourceId === system.resourceId)
.map(({ address }) => address),
functions: systemFunctions,
worldFunctions,
};
}),
);
Expand Down
76 changes: 40 additions & 36 deletions packages/cli/src/deploy/resolveConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,45 +41,49 @@ export async function resolveConfig({

const configSystems = await resolveSystems({ rootDir, config });

const systems = configSystems.map((system): System => {
const contractData = getContractData(`${system.label}.sol`, system.label, forgeOutDir);
const systems = configSystems
.filter((system) => !system.deploy.disabled)
.map((system): System => {
const contractData = getContractData(`${system.label}.sol`, system.label, forgeOutDir);

const systemFunctions = contractData.abi
.filter((item): item is typeof item & { type: "function" } => item.type === "function")
.map(toFunctionSignature)
.filter((sig) => !baseSystemFunctions.includes(sig))
.map((sig): WorldFunction => {
// TODO: figure out how to not duplicate contract behavior (https://github.com/latticexyz/mud/issues/1708)
const worldSignature = system.namespace === "" ? sig : `${system.namespace}__${sig}`;
return {
signature: worldSignature,
selector: toFunctionSelector(worldSignature),
systemId: system.systemId,
systemFunctionSignature: sig,
systemFunctionSelector: toFunctionSelector(sig),
};
});
const worldFunctions = system.deploy.registerWorldFunctions
? contractData.abi
.filter((item): item is typeof item & { type: "function" } => item.type === "function")
.map(toFunctionSignature)
.filter((sig) => !baseSystemFunctions.includes(sig))
.map((sig): WorldFunction => {
// TODO: figure out how to not duplicate contract behavior (https://github.com/latticexyz/mud/issues/1708)
const worldSignature = system.namespace === "" ? sig : `${system.namespace}__${sig}`;
return {
signature: worldSignature,
selector: toFunctionSelector(worldSignature),
systemId: system.systemId,
systemFunctionSignature: sig,
systemFunctionSelector: toFunctionSelector(sig),
};
})
: [];

// TODO: move to resolveSystems?
const allowedAddresses = system.accessList.filter((target): target is Hex => isHex(target));
const allowedSystemIds = system.accessList
.filter((target) => !isHex(target))
.map((label) => {
const system = configSystems.find((s) => s.label === label)!;
return system.systemId;
});
// TODO: move to resolveSystems?
const allowedAddresses = system.accessList.filter((target): target is Hex => isHex(target));
const allowedSystemIds = system.accessList
.filter((target) => !isHex(target))
.map((label) => {
const system = configSystems.find((s) => s.label === label)!;
return system.systemId;
});

return {
...system,
allowAll: system.openAccess,
allowedAddresses,
allowedSystemIds,
prepareDeploy: createPrepareDeploy(contractData.bytecode, contractData.placeholders),
deployedBytecodeSize: contractData.deployedBytecodeSize,
abi: contractData.abi,
functions: systemFunctions,
};
});
return {
...system,
allowAll: system.openAccess,
allowedAddresses,
allowedSystemIds,
prepareDeploy: createPrepareDeploy(contractData.bytecode, contractData.placeholders),
deployedBytecodeSize: contractData.deployedBytecodeSize,
abi: contractData.abi,
worldFunctions,
};
});

// Check for overlapping system IDs (since names get truncated when turning into IDs)
// TODO: move this into the world config resolve step once it resolves system IDs
Expand Down
11 changes: 9 additions & 2 deletions packages/world/ts/config/v2/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { CodegenInput, DeployInput, ModuleInput, SystemInput, WorldInput } from "./input";
import { CodegenInput, DeployInput, ModuleInput, SystemDeployInput, SystemInput, WorldInput } from "./input";

export const SYSTEM_DEPLOY_DEFAULTS = {
disabled: false,
registerWorldFunctions: true,
} as const satisfies Required<SystemDeployInput>;

export type SYSTEM_DEPLOY_DEFAULTS = typeof SYSTEM_DEPLOY_DEFAULTS;

export const SYSTEM_DEFAULTS = {
namespace: "",
openAccess: true,
accessList: [],
} as const satisfies Omit<Required<SystemInput>, "label" | "name">;
} as const satisfies Omit<Required<SystemInput>, "label" | "name" | "deploy">;

export type SYSTEM_DEFAULTS = typeof SYSTEM_DEFAULTS;

Expand Down
5 changes: 4 additions & 1 deletion packages/world/ts/config/v2/input.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { StoreInput, NamespaceInput as StoreNamespaceInput } from "@latticexyz/store/config/v2";
import { DynamicResolution, ValueWithType } from "./dynamicResolution";
import { Codegen } from "./output";
import { Codegen, SystemDeploy } from "./output";

export type SystemDeployInput = Partial<SystemDeploy>;

export type SystemInput = {
/**
Expand All @@ -22,6 +24,7 @@ export type SystemInput = {
readonly openAccess?: boolean;
/** An array of addresses or system names that can access the system */
readonly accessList?: readonly string[];
readonly deploy?: SystemDeployInput;
};

export type SystemsInput = {
Expand Down
15 changes: 15 additions & 0 deletions packages/world/ts/config/v2/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ export type Module = {
readonly artifactPath: string | undefined;
};

export type SystemDeploy = {
/**
* Whether or not to deploy the system.
* Defaults to `false`.
*/
readonly disabled: boolean;
/**
* Whether or not to register system functions on the world.
* System functions are prefixed with the system namespace when registering on the world, so system function names must be unique within their namespace.
* Defaults to `true`.
*/
readonly registerWorldFunctions: boolean;
};

export type System = {
/**
* Human-readable system label. Used as config keys, interface names, and filenames.
Expand All @@ -47,6 +61,7 @@ export type System = {
readonly openAccess: boolean;
/** An array of addresses or system names that can access the system */
readonly accessList: readonly string[];
readonly deploy: SystemDeploy;
};

export type Systems = {
Expand Down
12 changes: 12 additions & 0 deletions packages/world/ts/config/v2/system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ describe("resolveSystem", () => {
namespace: "",
name: "ExampleSystem" as string,
systemId: resourceToHex({ type: "system", namespace: "", name: "ExampleSystem" }),
deploy: {
disabled: false,
registerWorldFunctions: true,
},
} as const;

attest<typeof expected>(system).equals(expected);
Expand All @@ -33,6 +37,10 @@ describe("resolveSystem", () => {
namespace: "",
name: "ExampleSystem" as string,
systemId: resourceToHex({ type: "system", namespace: "", name: "ExampleSystem" }),
deploy: {
disabled: false,
registerWorldFunctions: true,
},
} as const;

attest<typeof expected>(system).equals(expected);
Expand All @@ -51,6 +59,10 @@ describe("resolveSystem", () => {
name: "ExampleSystem" as string,
systemId: resourceToHex({ type: "system", namespace: "", name: "ExampleSystem" }),
openAccess: false,
deploy: {
disabled: false,
registerWorldFunctions: true,
},
} as const;

attest<typeof expected>(system).equals(expected);
Expand Down
8 changes: 6 additions & 2 deletions packages/world/ts/config/v2/system.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SYSTEM_DEFAULTS } from "./defaults";
import { SYSTEM_DEFAULTS, SYSTEM_DEPLOY_DEFAULTS } from "./defaults";
import { SystemInput } from "./input";
import { hasOwnKey, mergeIfUndefined } from "@latticexyz/store/config/v2";
import { ErrorMessage, narrow, requiredKeyOf } from "@ark/util";
import { ErrorMessage, narrow, requiredKeyOf, show } from "@ark/util";
import { Hex } from "viem";
import { resourceToHex } from "@latticexyz/common";

Expand Down Expand Up @@ -52,6 +52,9 @@ export type resolveSystem<input> = input extends SystemInput
readonly systemId: Hex;
readonly openAccess: undefined extends input["openAccess"] ? SYSTEM_DEFAULTS["openAccess"] : input["openAccess"];
readonly accessList: undefined extends input["accessList"] ? SYSTEM_DEFAULTS["accessList"] : input["accessList"];
readonly deploy: show<
mergeIfUndefined<undefined extends input["deploy"] ? {} : input["deploy"], SYSTEM_DEPLOY_DEFAULTS>
>;
}
: never;

Expand All @@ -68,6 +71,7 @@ export function resolveSystem<input extends SystemInput>(input: input): resolveS
namespace,
name,
systemId,
deploy: mergeIfUndefined(input.deploy ?? {}, SYSTEM_DEPLOY_DEFAULTS),
},
SYSTEM_DEFAULTS,
) as never;
Expand Down
5 changes: 5 additions & 0 deletions packages/world/ts/config/v2/world.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ describe("defineWorld", () => {
namespace: "app",
name: "Example",
systemId: "0x737961707000000000000000000000004578616d706c65000000000000000000",
deploy: { disabled: false, registerWorldFunctions: true },
openAccess: true,
accessList: [],
},
Expand All @@ -592,6 +593,10 @@ describe("defineWorld", () => {
readonly systemId: \`0x\${string}\`
readonly openAccess: true
readonly accessList: readonly []
readonly deploy: {
readonly disabled: false
readonly registerWorldFunctions: true
}
}
}`);
});
Expand Down
19 changes: 11 additions & 8 deletions packages/world/ts/node/render-solidity/worldgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ export async function worldgen({

const outputPath = path.join(outDir, config.codegen.worldInterfaceName + ".sol");

const systems = (await resolveSystems({ rootDir, config })).map((system) => {
const interfaceName = `I${system.label}`;
return {
...system,
interfaceName,
interfacePath: path.join(path.dirname(outputPath), `${interfaceName}.sol`),
};
});
const systems = (await resolveSystems({ rootDir, config }))
// TODO: move to codegen option or generate "system manifest" and codegen from that
.filter((system) => system.deploy.registerWorldFunctions)
.map((system) => {
const interfaceName = `I${system.label}`;
return {
...system,
interfaceName,
interfacePath: path.join(path.dirname(outputPath), `${interfaceName}.sol`),
};
});

const worldImports = systems.map(
(system): ImportDatum => ({
Expand Down

0 comments on commit 86a8104

Please sign in to comment.