create-mud 2.2.15-7482a5ae322f26278722081838b6103ce6d264e6 → 2.2.15-7fa68f6717ecd3fc2c734ca31c842c1242175cbe

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 (46) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/templates/react/gitignore +5 -14
  4. package/dist/templates/react/mprocs.yaml +1 -9
  5. package/dist/templates/react/package.json +4 -28
  6. package/dist/templates/react/packages/client/gitignore +2 -0
  7. package/dist/templates/react/packages/client/package.json +12 -21
  8. package/dist/templates/react/packages/client/src/App.tsx +100 -38
  9. package/dist/templates/react/packages/client/src/MUDContext.tsx +21 -0
  10. package/dist/templates/react/packages/client/src/index.tsx +31 -17
  11. package/dist/templates/react/packages/client/src/mud/createSystemCalls.ts +56 -0
  12. package/dist/templates/react/packages/client/src/mud/getNetworkConfig.ts +76 -0
  13. package/dist/templates/react/packages/client/src/mud/setup.ts +18 -0
  14. package/dist/templates/react/packages/client/src/mud/setupNetwork.ts +101 -0
  15. package/dist/templates/react/packages/client/src/mud/supportedChains.ts +20 -0
  16. package/dist/templates/react/packages/client/vite.config.ts +6 -0
  17. package/dist/templates/react/packages/contracts/gitignore +8 -12
  18. package/dist/templates/react/packages/contracts/mud.config.ts +8 -13
  19. package/dist/templates/react/packages/contracts/package.json +7 -8
  20. package/dist/templates/react/packages/contracts/script/PostDeploy.s.sol +9 -1
  21. package/dist/templates/react/packages/contracts/src/codegen/index.sol +1 -1
  22. package/dist/templates/react/packages/contracts/src/codegen/tables/Tasks.sol +522 -0
  23. package/dist/templates/react/packages/contracts/src/codegen/world/{IMoveSystem.sol → ITasksSystem.sol} +9 -5
  24. package/dist/templates/react/packages/contracts/src/codegen/world/IWorld.sol +2 -2
  25. package/dist/templates/react/packages/contracts/src/systems/TasksSystem.sol +24 -0
  26. package/dist/templates/react/packages/contracts/test/TasksTest.t.sol +30 -0
  27. package/dist/templates/react/packages/contracts/worlds.json +1 -1
  28. package/package.json +1 -1
  29. package/dist/templates/react/packages/client/postcss.config.cjs +0 -6
  30. package/dist/templates/react/packages/client/src/GameMap.tsx +0 -78
  31. package/dist/templates/react/packages/client/src/Providers.tsx +0 -44
  32. package/dist/templates/react/packages/client/src/common.ts +0 -6
  33. package/dist/templates/react/packages/client/src/mud/Explorer.tsx +0 -32
  34. package/dist/templates/react/packages/client/src/mud/getWorldDeploy.ts +0 -23
  35. package/dist/templates/react/packages/client/src/mud/stash.ts +0 -4
  36. package/dist/templates/react/packages/client/src/mud/useSyncProgress.ts +0 -21
  37. package/dist/templates/react/packages/client/src/mud/useWorldContract.ts +0 -45
  38. package/dist/templates/react/packages/client/src/ui/icons/ArrowDownIcon.tsx +0 -22
  39. package/dist/templates/react/packages/client/src/ui/icons/MUDIcon.tsx +0 -25
  40. package/dist/templates/react/packages/client/src/useKeyboardMovement.ts +0 -26
  41. package/dist/templates/react/packages/client/src/wagmiConfig.ts +0 -49
  42. package/dist/templates/react/packages/client/tailwind.config.ts +0 -10
  43. package/dist/templates/react/packages/contracts/out/IWorld.sol/IWorld.abi.json +0 -2021
  44. package/dist/templates/react/packages/contracts/src/MoveSystem.sol +0 -26
  45. package/dist/templates/react/packages/contracts/src/codegen/common.sol +0 -11
  46. package/dist/templates/react/packages/contracts/src/codegen/tables/Position.sol +0 -318
@@ -4,7 +4,7 @@ pragma solidity >=0.8.24;
4
4
  /* Autogenerated file. Do not edit manually. */
5
5
 
6
6
  import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
7
- import { IMoveSystem } from "./IMoveSystem.sol";
7
+ import { ITasksSystem } from "./ITasksSystem.sol";
8
8
 
9
9
  /**
10
10
  * @title IWorld
@@ -13,4 +13,4 @@ import { IMoveSystem } from "./IMoveSystem.sol";
13
13
  * that are dynamically registered in the World during deployment.
14
14
  * @dev This is an autogenerated file; do not edit manually.
15
15
  */
16
- interface IWorld is IBaseWorld, IMoveSystem {}
16
+ interface IWorld is IBaseWorld, ITasksSystem {}
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity >=0.8.24;
3
+
4
+ import { System } from "@latticexyz/world/src/System.sol";
5
+ import { Tasks, TasksData } from "../codegen/index.sol";
6
+
7
+ contract TasksSystem is System {
8
+ function addTask(string memory description) public returns (bytes32 id) {
9
+ id = keccak256(abi.encode(block.prevrandao, _msgSender(), description));
10
+ Tasks.set(id, TasksData({ description: description, createdAt: block.timestamp, completedAt: 0 }));
11
+ }
12
+
13
+ function completeTask(bytes32 id) public {
14
+ Tasks.setCompletedAt(id, block.timestamp);
15
+ }
16
+
17
+ function resetTask(bytes32 id) public {
18
+ Tasks.setCompletedAt(id, 0);
19
+ }
20
+
21
+ function deleteTask(bytes32 id) public {
22
+ Tasks.deleteRecord(id);
23
+ }
24
+ }
@@ -0,0 +1,30 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity >=0.8.24;
3
+
4
+ import "forge-std/Test.sol";
5
+ import { MudTest } from "@latticexyz/world/test/MudTest.t.sol";
6
+
7
+ import { IWorld } from "../src/codegen/world/IWorld.sol";
8
+ import { Tasks, TasksData } from "../src/codegen/index.sol";
9
+
10
+ contract TasksTest is MudTest {
11
+ function testWorldExists() public {
12
+ uint256 codeSize;
13
+ address addr = worldAddress;
14
+ assembly {
15
+ codeSize := extcodesize(addr)
16
+ }
17
+ assertTrue(codeSize > 0);
18
+ }
19
+
20
+ function testTasks() public {
21
+ // Expect task to exist that we created during PostDeploy script
22
+ TasksData memory task = Tasks.get("1");
23
+ assertEq(task.description, "Walk the dog");
24
+ assertEq(task.completedAt, 0);
25
+
26
+ // Expect the task to be completed after calling completeTask from our TasksSystem
27
+ IWorld(worldAddress).app__completeTask("1");
28
+ assertEq(Tasks.getCompletedAt("1"), block.timestamp);
29
+ }
30
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "31337": {
3
- "address": "0xfdf868ea710ffd8cd33b829c5aff79edd15ecd5f"
3
+ "address": "0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b"
4
4
  }
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mud",
3
- "version": "2.2.15-7482a5ae322f26278722081838b6103ce6d264e6",
3
+ "version": "2.2.15-7fa68f6717ecd3fc2c734ca31c842c1242175cbe",
4
4
  "description": "Create a new MUD project",
5
5
  "license": "MIT",
6
6
  "author": "Lattice <mud@lattice.xyz>",
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- plugins: {
3
- tailwindcss: {},
4
- autoprefixer: {},
5
- },
6
- };
@@ -1,78 +0,0 @@
1
- import { serialize, useAccount } from "wagmi";
2
- import { useKeyboardMovement } from "./useKeyboardMovement";
3
- import { Address, Hex, hexToBigInt, keccak256 } from "viem";
4
- import { ArrowDownIcon } from "./ui/icons/ArrowDownIcon";
5
- import { twMerge } from "tailwind-merge";
6
- import { Direction } from "./common";
7
- import mudConfig from "contracts/mud.config";
8
-
9
- export type Props = {
10
- readonly players?: readonly {
11
- readonly player: Address;
12
- readonly x: number;
13
- readonly y: number;
14
- }[];
15
-
16
- readonly onMove?: (direction: Direction) => void;
17
- };
18
-
19
- const size = 40;
20
- const scale = 100 / size;
21
-
22
- function getColorAngle(seed: Hex) {
23
- return Number(hexToBigInt(keccak256(seed)) % 360n);
24
- }
25
-
26
- const rotateClassName = {
27
- North: "rotate-0",
28
- East: "rotate-90",
29
- South: "rotate-180",
30
- West: "-rotate-90",
31
- } as const satisfies Record<Direction, `${"" | "-"}rotate-${number}`>;
32
-
33
- export function GameMap({ players = [], onMove }: Props) {
34
- const { address: userAddress } = useAccount();
35
- useKeyboardMovement(onMove);
36
- return (
37
- <div className="aspect-square w-full max-w-[40rem]">
38
- <div className="relative w-full h-full border-8 border-black/10">
39
- {onMove
40
- ? mudConfig.enums.Direction.map((direction) => (
41
- <button
42
- key={direction}
43
- title={`Move ${direction.toLowerCase()}`}
44
- className={twMerge(
45
- "outline-0 absolute inset-0 cursor-pointer grid p-4",
46
- rotateClassName[direction],
47
- "transition bg-gradient-to-t from-transparent via-transparent to-blue-50 text-blue-400 opacity-0 hover:opacity-40 active:opacity-100",
48
- )}
49
- style={{ clipPath: "polygon(0% 0%, 100% 0%, 50% 50%)" }}
50
- onClick={() => onMove(direction)}
51
- >
52
- <ArrowDownIcon className="rotate-180 text-4xl self-start justify-self-center" />
53
- </button>
54
- ))
55
- : null}
56
-
57
- {players.map((player) => (
58
- <div
59
- key={player.player}
60
- className="absolute bg-current"
61
- style={{
62
- color: `hwb(${getColorAngle(player.player)} 40% 20%)`,
63
- width: `${scale}%`,
64
- height: `${scale}%`,
65
- left: `${((((player.x + size / 2) % size) + size) % size) * scale}%`,
66
- top: `${((size - ((player.y + size / 2) % size)) % size) * scale}%`,
67
- }}
68
- title={serialize(player, null, 2)}
69
- >
70
- {player.player.toLowerCase() === userAddress?.toLowerCase() ? (
71
- <div className="w-full h-full bg-current animate-ping opacity-50" />
72
- ) : null}
73
- </div>
74
- ))}
75
- </div>
76
- </div>
77
- );
78
- }
@@ -1,44 +0,0 @@
1
- import { WagmiProvider } from "wagmi";
2
- import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
3
- import { ReactNode } from "react";
4
- import { createSyncAdapter } from "@latticexyz/store-sync/internal";
5
- import { SyncProvider } from "@latticexyz/store-sync/react";
6
- import { stash } from "./mud/stash";
7
- import { Address } from "viem";
8
- import { defineConfig, EntryKitProvider } from "@latticexyz/entrykit/internal";
9
- import { wagmiConfig } from "./wagmiConfig";
10
- import { chainId } from "./common";
11
-
12
- const queryClient = new QueryClient();
13
-
14
- export type Props = {
15
- worldDeploy: {
16
- address: Address;
17
- blockNumber: bigint | null;
18
- };
19
- children: ReactNode;
20
- };
21
-
22
- export function Providers({ worldDeploy, children }: Props) {
23
- return (
24
- <WagmiProvider config={wagmiConfig}>
25
- <QueryClientProvider client={queryClient}>
26
- <EntryKitProvider
27
- config={defineConfig({
28
- chainId,
29
- worldAddress: worldDeploy.address,
30
- })}
31
- >
32
- <SyncProvider
33
- chainId={chainId}
34
- address={worldDeploy.address}
35
- startBlock={worldDeploy.blockNumber ?? undefined}
36
- adapter={createSyncAdapter({ stash })}
37
- >
38
- {children}
39
- </SyncProvider>
40
- </EntryKitProvider>
41
- </QueryClientProvider>
42
- </WagmiProvider>
43
- );
44
- }
@@ -1,6 +0,0 @@
1
- import mudConfig from "contracts/mud.config";
2
-
3
- export const chainId = parseInt(import.meta.env.VITE_CHAIN_ID) || 31337;
4
- export const url = new URL(window.location.href);
5
-
6
- export type Direction = (typeof mudConfig.enums.Direction)[number];
@@ -1,32 +0,0 @@
1
- import { useState } from "react";
2
- import { twMerge } from "tailwind-merge";
3
- import { useEntryKitConfig } from "@latticexyz/entrykit/internal";
4
- import { MUDIcon } from "../ui/icons/MUDIcon";
5
-
6
- export function Explorer() {
7
- const [open, setOpen] = useState(false);
8
- const { chain, worldAddress } = useEntryKitConfig();
9
- const explorerUrl = chain.blockExplorers?.worldsExplorer?.url;
10
- if (!explorerUrl) return null;
11
-
12
- return (
13
- <div className="fixed bottom-0 inset-x-0 flex flex-col opacity-80 transition hover:opacity-100">
14
- <button
15
- type="button"
16
- onClick={() => setOpen(!open)}
17
- className="outline-none flex justify-end gap-2 p-2 font-medium leading-none text-black"
18
- >
19
- {open ? (
20
- <>Close</>
21
- ) : (
22
- <>
23
- Explore <MUDIcon className="text-orange-500" />
24
- </>
25
- )}
26
- </button>
27
- {open ? (
28
- <iframe src={`${explorerUrl}/${worldAddress}`} className={twMerge("bg-black", open ? "h-[50vh]" : "h-0")} />
29
- ) : null}
30
- </div>
31
- );
32
- }
@@ -1,23 +0,0 @@
1
- import { Address } from "viem";
2
-
3
- export async function getWorldDeploy(chainId: number): Promise<{
4
- chainId: number;
5
- address: Address;
6
- blockNumber: bigint | null;
7
- }> {
8
- // TODO: figure out how to catch vite:import-analysis error when this file is missing
9
- const { default: worlds } = await import("contracts/worlds.json").catch((error) => {
10
- console.debug("Could not import worlds.json", error);
11
- return { default: null };
12
- });
13
- const deploy = worlds?.[`${chainId}`];
14
- if (!deploy) {
15
- throw new Error(`No world deploy found for chain ${chainId} in "worlds.json".`);
16
- }
17
- console.log("chain:", chainId, "world:", deploy);
18
- return {
19
- chainId,
20
- address: deploy.address,
21
- blockNumber: deploy.blockNumber != null ? BigInt(deploy.blockNumber) : null,
22
- };
23
- }
@@ -1,4 +0,0 @@
1
- import { createStash } from "@latticexyz/stash/internal";
2
- import config from "contracts/mud.config";
3
-
4
- export const stash = createStash(config);
@@ -1,21 +0,0 @@
1
- import { stash } from "./stash";
2
- import { initialProgress, SyncProgress } from "@latticexyz/store-sync/internal";
3
- import { SyncStep } from "@latticexyz/store-sync";
4
- import { useMemo } from "react";
5
- import { useRecord } from "@latticexyz/stash/react";
6
-
7
- export function useSyncProgress() {
8
- const progress = useRecord({
9
- stash,
10
- table: SyncProgress,
11
- key: {},
12
- defaultValue: initialProgress,
13
- });
14
- return useMemo(
15
- () => ({
16
- ...progress,
17
- isLive: progress.step === SyncStep.LIVE,
18
- }),
19
- [progress],
20
- );
21
- }
@@ -1,45 +0,0 @@
1
- import { useClient } from "wagmi";
2
- import { chainId } from "../common";
3
- import { Account, Chain, Client, GetContractReturnType, Transport, getContract } from "viem";
4
- import { useQuery } from "@tanstack/react-query";
5
- import { useSessionClient, useEntryKitConfig } from "@latticexyz/entrykit/internal";
6
- import { observer } from "@latticexyz/explorer/observer";
7
- import worldAbi from "contracts/out/IWorld.sol/IWorld.abi.json";
8
-
9
- export function useWorldContract():
10
- | GetContractReturnType<
11
- typeof worldAbi,
12
- {
13
- public: Client<Transport, Chain>;
14
- wallet: Client<Transport, Chain, Account>;
15
- }
16
- >
17
- | undefined {
18
- const { worldAddress } = useEntryKitConfig();
19
- const client = useClient({ chainId });
20
- const { data: sessionClient } = useSessionClient();
21
-
22
- const { data: worldContract } = useQuery({
23
- queryKey: ["worldContract", worldAddress, client?.uid, sessionClient?.uid],
24
- queryFn: () => {
25
- if (!client || !sessionClient) {
26
- throw new Error("Not connected.");
27
- }
28
-
29
- return getContract({
30
- abi: worldAbi,
31
- address: worldAddress,
32
- client: {
33
- public: client,
34
- wallet: sessionClient.extend(observer()),
35
- },
36
- });
37
- },
38
- staleTime: Infinity,
39
- refetchOnMount: false,
40
- refetchOnReconnect: false,
41
- refetchOnWindowFocus: false,
42
- });
43
-
44
- return worldContract;
45
- }
@@ -1,22 +0,0 @@
1
- import { DetailedHTMLProps, SVGAttributes } from "react";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export type Props = DetailedHTMLProps<SVGAttributes<SVGSVGElement>, SVGSVGElement>;
5
-
6
- export function ArrowDownIcon({ className, ...props }: Props) {
7
- return (
8
- <svg
9
- className={twMerge("h-[1em] w-[1em]", className)}
10
- xmlns="http://www.w3.org/2000/svg"
11
- viewBox="0 0 16 16"
12
- fill="currentColor"
13
- {...props}
14
- >
15
- <path
16
- fillRule="evenodd"
17
- d="M8 2a.75.75 0 0 1 .75.75v8.69l3.22-3.22a.75.75 0 1 1 1.06 1.06l-4.5 4.5a.75.75 0 0 1-1.06 0l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.22 3.22V2.75A.75.75 0 0 1 8 2Z"
18
- clipRule="evenodd"
19
- />
20
- </svg>
21
- );
22
- }
@@ -1,25 +0,0 @@
1
- import { DetailedHTMLProps, SVGAttributes } from "react";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export type Props = DetailedHTMLProps<SVGAttributes<SVGSVGElement>, SVGSVGElement>;
5
-
6
- export function MUDIcon({ className, ...props }: Props) {
7
- return (
8
- <svg
9
- xmlns="http://www.w3.org/2000/svg"
10
- viewBox="0 0 8 8"
11
- fill="currentColor"
12
- shapeRendering="crispEdges"
13
- className={twMerge("-my-[0.125em] h-[1.25em] w-[1.25em]", className)}
14
- {...props}
15
- >
16
- {/* eslint-disable-next-line max-len */}
17
- <path d="M0 0h1v1H0zm0 1h1v1H0zm0 1h1v1H0zm0 1h1v1H0zm0 1h1v1H0zm0 1h1v1H0zm0 1h1v1H0zm0 1h1v1H0zm1 0h1v1H1zm1 0h1v1H2zm1 0h1v1H3zm1 0h1v1H4zm1 0h1v1H5zm2-1h1v1H7zm0 1h1v1H7zM6 7h1v1H6zm1-2h1v1H7zm0-1h1v1H7zm0-1h1v1H7z" />
18
- <path
19
- d="M2 2h1v1H2zm0 1h1v1H2zm0 1h1v1H2zm0 1h1v1H2zm1-3h1v1H3zm1 0h1v1H4zm1 0h1v1H5zm0 1h1v1H5zm0 1h1v1H5zm0 1h1v1H5zM4 5h1v1H4zM3 5h1v1H3z"
20
- opacity=".5"
21
- />
22
- <path d="M7 2h1v1H7zm0-1h1v1H7zM1 0h1v1H1zm1 0h1v1H2zm1 0h1v1H3zm1 0h1v1H4zm1 0h1v1H5zm1 0h1v1H6zm1 0h1v1H7z" />
23
- </svg>
24
- );
25
- }
@@ -1,26 +0,0 @@
1
- import { useEffect } from "react";
2
- import { Direction } from "./common";
3
-
4
- const keys = new Map<KeyboardEvent["key"], Direction>([
5
- ["ArrowUp", "North"],
6
- ["ArrowRight", "East"],
7
- ["ArrowDown", "South"],
8
- ["ArrowLeft", "West"],
9
- ]);
10
-
11
- export const useKeyboardMovement = (move: undefined | ((direction: Direction) => void)) => {
12
- useEffect(() => {
13
- if (!move) return;
14
-
15
- const listener = (event: KeyboardEvent) => {
16
- const direction = keys.get(event.key);
17
- if (direction == null) return;
18
-
19
- event.preventDefault();
20
- move(direction);
21
- };
22
-
23
- window.addEventListener("keydown", listener);
24
- return () => window.removeEventListener("keydown", listener);
25
- }, [move]);
26
- };
@@ -1,49 +0,0 @@
1
- import { Chain, http, webSocket } from "viem";
2
- import { anvil } from "viem/chains";
3
- import { createWagmiConfig } from "@latticexyz/entrykit/internal";
4
- import { rhodolite, garnet, redstone } from "@latticexyz/common/chains";
5
- import { chainId } from "./common";
6
-
7
- const chains = [
8
- redstone,
9
- garnet,
10
- rhodolite,
11
- {
12
- ...anvil,
13
- contracts: {
14
- ...anvil.contracts,
15
- paymaster: {
16
- address: "0xf03E61E7421c43D9068Ca562882E98d1be0a6b6e",
17
- },
18
- },
19
- blockExplorers: {
20
- default: {} as never,
21
- worldsExplorer: {
22
- name: "MUD Worlds Explorer",
23
- url: "http://localhost:13690/anvil/worlds",
24
- },
25
- },
26
- },
27
- ] as const satisfies Chain[];
28
-
29
- const transports = {
30
- [anvil.id]: webSocket(),
31
- [garnet.id]: http(),
32
- [rhodolite.id]: http(),
33
- [redstone.id]: http(),
34
- } as const;
35
-
36
- export const wagmiConfig = createWagmiConfig({
37
- chainId,
38
- // TODO: swap this with another default project ID or leave empty
39
- walletConnectProjectId: "14ce88fdbc0f9c294e26ec9b4d848e44",
40
- appName: document.title,
41
- chains,
42
- transports,
43
- pollingInterval: {
44
- [anvil.id]: 2000,
45
- [garnet.id]: 2000,
46
- [rhodolite.id]: 2000,
47
- [redstone.id]: 2000,
48
- },
49
- });
@@ -1,10 +0,0 @@
1
- import { Config } from "tailwindcss/types/config";
2
-
3
- export default {
4
- content: ["./**/*.{html,js,jsx,ts,tsx}", "!./node_modules"],
5
- theme: {},
6
- plugins: [],
7
- future: {
8
- hoverOnlyWhenSupported: true,
9
- },
10
- } satisfies Config;