Skip to content

Commit

Permalink
feat(cli): deploy custom world (#3131)
Browse files Browse the repository at this point in the history
  • Loading branch information
holic authored Sep 10, 2024
1 parent b9c61a9 commit 8546452
Showing 42 changed files with 1,047 additions and 76 deletions.
24 changes: 24 additions & 0 deletions .changeset/heavy-mice-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
"@latticexyz/cli": patch
"@latticexyz/world": patch
---

MUD config now supports a `deploy.customWorld` option that, when used with the CLI, will deploy the specified custom World implementation.
Custom implementations must still follow [the World protocol](https://github.com/latticexyz/mud/tree/main/packages/world/ts/protocol-snapshots).

If you want to extend the world with new functions or override existing registered functions, we recommend using [root systems](https://mud.dev/world/systems#root-systems).
However, there are rare cases where this may not be enough to modify the native/internal World behavior.
Note that deploying a custom World opts out of the world factory, deterministic world deploys, and upgradeable implementation proxy.

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

export default defineWorld({
customWorld: {
// path to custom world source from project root
sourcePath: "src/CustomWorld.sol",
// custom world contract name
name: "CustomWorld",
},
});
```
6 changes: 5 additions & 1 deletion .github/workflows/examples.yml
Original file line number Diff line number Diff line change
@@ -21,7 +21,11 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
example: [minimal, multiple-namespaces]
example: [minimal, multiple-namespaces, custom-world]
env:
DEBUG: mud:*
# anvil default, prefunded account
PRIVATE_KEY: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

steps:
- name: Checkout
21 changes: 17 additions & 4 deletions docs/pages/config/reference.mdx
Original file line number Diff line number Diff line change
@@ -128,18 +128,18 @@ The following options are available in both single- and multiple-namespace modes

<Params title="Module config options">
<Param name="artifactPath">Relative path to the module's compiled JSON artifact (usually in `out`) or an import path if using a module from an npm package. This path is resolved using [Node's module `require` API](https://nodejs.org/api/modules.html#modulerequireid).</Param>

<Param name="root">Whether or not to install this as a root module. Defaults to `false`.</Param>

<Param name="args">
A list of arguments used to call the module's install function. Each argument is a structure with two fields:
<Params title="Structure in each argument">

<Params>
<Param name="type">Solidity data type.</Param>
<Param name="value">
The value.
To encode a complex data type, such as a structure or an array, you can use Viem's [`encodeAbiParameters`](https://viem.sh/docs/abi/encodeAbiParameters.html).
</Param>
</Params>

</Param>
</Params>

@@ -154,6 +154,7 @@ The following options are available in both single- and multiple-namespace modes
<Param name="userTypesFilename">Filename relative to `outputDirectory` to codegen enums into. Defaults to `"common.sol"`.</Param>
<Param name="worldgenDirectory">Output directory of world interfaces relative to `outputDirectory`. Defaults to `"world"`.</Param>
</Params>

</Param>
<Param name="deploy">
Customize how to deploy the world.
@@ -162,7 +163,19 @@ The following options are available in both single- and multiple-namespace modes
<Param name="deploysDirectory">Directory, relative to project root, to write the deployment artifacts to. Defaults to `"deploys"`.</Param>
<Param name="postDeployScript">Script name to execute after the deployment is complete. Defaults to `"PostDeploy"`.</Param>
<Param name="worldsFile">JSON filename, relative to project root, to write per-chain world deployment addresses. Defaults to `"worlds.json"`.</Param>
<Param name="upgradeableWorldImplementation">Wheter or not to deploy the world with an upgradeable proxy, allowing for the core implementation to be upgraded. Defaults to `false`, but [we recommend `true`](/guides/best-practices/deployment-settings).</Param>
<Param name="upgradeableWorldImplementation">Whether or not to deploy the world with an upgradeable proxy, allowing for the core implementation to be upgraded. Defaults to `false`, but [we recommend `true`](/guides/best-practices/deployment-settings).</Param>
<Param name="customWorld">
Deploy the World using a custom implementation. This world must implement the same interface as `World.sol` so that it can initialize core modules, etc.
If you want to extend the world with new functions or override existing registered functions, we recommend using [root systems](/world/systems#root-systems).
However, there are rare cases where this may not be enough to modify the native/internal World behavior.
Note that deploying a custom World opts out of the world factory, deterministic world deploys, and upgradeable implementation proxy.

<Params>
<Param name="sourcePath">Path to custom world source file relative to project root dir.</Param>
<Param name="name">Contract name in custom world source file.</Param>
</Params>

</Param>
</Params>

</Param>
12 changes: 12 additions & 0 deletions examples/custom-world/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
out/
cache/
node_modules/
bindings/
artifacts/
broadcast/

pnpm-lock.yaml

# Ignore MUD deploy artifacts
deploys/**/*.json
worlds.json
8 changes: 8 additions & 0 deletions examples/custom-world/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"plugins": ["prettier-plugin-solidity"],
"printWidth": 120,
"semi": true,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true
}
12 changes: 12 additions & 0 deletions examples/custom-world/.solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": ["solhint:recommended", "mud"],
"plugins": ["mud"],
"rules": {
"compiler-version": ["error", ">=0.8.0"],
"avoid-low-level-calls": "off",
"no-inline-assembly": "off",
"func-visibility": ["warn", { "ignoreConstructors": true }],
"no-empty-blocks": "off",
"no-complex-fallback": "off"
}
}
29 changes: 29 additions & 0 deletions examples/custom-world/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[profile.default]
solc = "0.8.24"
ffi = false
fuzz_runs = 256
optimizer = true
optimizer_runs = 3000
verbosity = 2
src = "src"
test = "test"
out = "out"
allow_paths = [
# pnpm symlinks to the project root's node_modules
"../../node_modules",
# template uses linked mud packages from within the mud monorepo
"../../../../packages",
# projects created from this template and using linked mud packages
"../../../mud/packages",
]
extra_output_files = [
"abi",
"evm.bytecode"
]
fs_permissions = [{ access = "read", path = "./"}]

[profile.garnet]
eth_rpc_url = "https://rpc.garnetchain.com"

[profile.redstone]
eth_rpc_url = "https://rpc.redstonechain.com"
22 changes: 22 additions & 0 deletions examples/custom-world/mud.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineWorld } from "@latticexyz/world";

export default defineWorld({
namespace: "app",
tables: {
Tasks: {
schema: {
id: "bytes32",
createdAt: "uint256",
completedAt: "uint256",
description: "string",
},
key: ["id"],
},
},
deploy: {
customWorld: {
sourcePath: "src/CustomWorld.sol",
name: "CustomWorld",
},
},
});
35 changes: 35 additions & 0 deletions examples/custom-world/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "mud-example-custom-world",
"version": "0.0.0",
"private": true,
"license": "MIT",
"scripts": {
"build": "mud build",
"clean": "forge clean && shx rm -rf src/**/codegen",
"deploy:garnet": "mud deploy --profile=garnet",
"deploy:local": "mud deploy",
"deploy:redstone": "mud deploy --profile=redstone",
"dev": "mud dev-contracts",
"lint": "pnpm run prettier && pnpm run solhint",
"prettier": "prettier --write 'src/**/*.sol'",
"solhint": "solhint --config ./.solhint.json 'src/**/*.sol' --fix",
"test": "tsc --noEmit && mud test"
},
"dependencies": {
"@latticexyz/schema-type": "link:../../packages/schema-type",
"@latticexyz/store": "link:../../packages/store",
"@latticexyz/world": "link:../../packages/world"
},
"devDependencies": {
"@latticexyz/cli": "link:../../packages/cli",
"@types/node": "^18.15.11",
"ds-test": "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0",
"forge-std": "https://github.com/foundry-rs/forge-std.git#74cfb77e308dd188d2f58864aaf44963ae6b88b1",
"prettier": "3.2.5",
"prettier-plugin-solidity": "1.3.1",
"shx": "^0.3.4",
"solhint": "^3.3.7",
"solhint-config-mud": "link:../../packages/solhint-config-mud",
"solhint-plugin-mud": "link:../../packages/solhint-plugin-mud"
}
}
1 change: 1 addition & 0 deletions examples/custom-world/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# this file forces pnpm to treat this separately from the monorepo root workspace
3 changes: 3 additions & 0 deletions examples/custom-world/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ds-test/=node_modules/ds-test/src/
forge-std/=node_modules/forge-std/src/
@latticexyz/=node_modules/@latticexyz/
33 changes: 33 additions & 0 deletions examples/custom-world/script/PostDeploy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;

import { Script } from "forge-std/Script.sol";
import { console } from "forge-std/console.sol";
import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";

import { IWorld } from "../src/codegen/world/IWorld.sol";
import { Tasks, TasksData } from "../src/codegen/index.sol";

contract PostDeploy is Script {
function run(address worldAddress) external {
// Specify a store so that you can use tables directly in PostDeploy
StoreSwitch.setStoreAddress(worldAddress);

// Load the private key from the `PRIVATE_KEY` environment variable (in .env)
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

// Start broadcasting transactions from the deployer account
vm.startBroadcast(deployerPrivateKey);

// We can set table records directly
Tasks.set("1", TasksData({ description: "Walk the dog", createdAt: block.timestamp, completedAt: 0 }));

// Or we can call our own systems
IWorld(worldAddress).app__addTask("Take out the trash");

bytes32 key = IWorld(worldAddress).app__addTask("Do the dishes");
IWorld(worldAddress).app__completeTask(key);

vm.stopBroadcast();
}
}
10 changes: 10 additions & 0 deletions examples/custom-world/src/CustomWorld.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 { World } from "@latticexyz/world/src/World.sol";

contract CustomWorld is World {
function helloWorld() public pure returns (string memory) {
return "Hello world!";
}
}
6 changes: 6 additions & 0 deletions examples/custom-world/src/codegen/index.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 8546452

Please sign in to comment.