From d3ab5c3783265b3e82b76157bccedeae6b0445e1 Mon Sep 17 00:00:00 2001 From: alvarius Date: Fri, 6 Sep 2024 22:02:55 +0100 Subject: [PATCH 01/20] feat(cli): speed up dev deploy with temporary automine during deploy (#3130) --- .changeset/real-pianos-yell.md | 5 ++ packages/cli/src/runDeploy.ts | 8 ++++ packages/cli/src/utils/enableAutomine.ts | 59 ++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 .changeset/real-pianos-yell.md create mode 100644 packages/cli/src/utils/enableAutomine.ts diff --git a/.changeset/real-pianos-yell.md b/.changeset/real-pianos-yell.md new file mode 100644 index 0000000000..130da6a63b --- /dev/null +++ b/.changeset/real-pianos-yell.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/cli": patch +--- + +Speed up deployment in development by temporarily enabling automine mode for the duration of the deployment. diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 1dc7be3cb8..09566279bd 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -16,6 +16,7 @@ import { WorldDeploy } from "./deploy/common"; import { build } from "./build"; import { kmsKeyToAccount } from "@latticexyz/common/kms"; import { configToModules } from "./deploy/configToModules"; +import { enableAutomine } from "./utils/enableAutomine"; export const deployOptions = { configPath: { type: "string", desc: "Path to the MUD config file" }, @@ -133,6 +134,9 @@ export async function runDeploy(opts: DeployOptions): Promise { console.log("Deploying from", client.account.address); + // Attempt to enable automine for the duration of the deploy. Noop if automine is not available. + const { reset: resetMiningMode } = await enableAutomine(client); + const startTime = Date.now(); const worldDeploy = await deploy({ deployerAddress: opts.deployerAddress as Hex | undefined, @@ -155,6 +159,10 @@ export async function runDeploy(opts: DeployOptions): Promise { opts.kms ? true : false, ); } + + // Reset mining mode after deploy + await resetMiningMode(); + console.log(chalk.green("Deployment completed in", (Date.now() - startTime) / 1000, "seconds")); const deploymentInfo = { diff --git a/packages/cli/src/utils/enableAutomine.ts b/packages/cli/src/utils/enableAutomine.ts new file mode 100644 index 0000000000..15c7a2e498 --- /dev/null +++ b/packages/cli/src/utils/enableAutomine.ts @@ -0,0 +1,59 @@ +import { getAutomine, getBlock, setAutomine, setIntervalMining } from "viem/actions"; +import { debug, error } from "../debug"; +import { Client } from "viem"; +import { getAction } from "viem/utils"; + +type MiningMode = + | { + type: "automine"; + } + | { + type: "interval"; + blockTime: number; + }; + +export type EnableAutomineResult = { reset: () => Promise }; + +export async function enableAutomine(client: Client): Promise { + try { + debug("Enabling automine"); + const prevMiningMode = await getMiningMode(client); + await setMiningMode(client, { type: "automine" }); + return { + reset: () => { + debug("Disabling automine"); + return setMiningMode(client, prevMiningMode); + }, + }; + } catch (e) { + debug("Skipping automine"); + error(e); + return { reset: async () => void 0 }; + } +} + +async function getMiningMode(client: Client): Promise { + const localClient = { mode: "anvil", ...client }; // set default mode to "anvil", potential error is caught by enableAutomine + const isAutomine = await getAction(localClient, getAutomine, "getAutomine")({}); + if (isAutomine) { + return { type: "automine" }; + } + + const blockTime = await getBlockTime(client); + return { type: "interval", blockTime }; +} + +async function setMiningMode(client: Client, miningMode: MiningMode): Promise { + if (miningMode.type === "automine") { + await getAction(client, setAutomine, "setAutomine")(true); + } else { + await getAction(client, setIntervalMining, "setIntervalMining")({ interval: miningMode.blockTime }); + } +} + +async function getBlockTime(client: Client): Promise { + const latestBlock = await getAction(client, getBlock, "getBlock")({ blockTag: "latest" }); + const previousBlock = await getAction(client, getBlock, "getBlock")({ blockNumber: latestBlock.number - 1n }); + const blockTime = latestBlock.timestamp - previousBlock.timestamp; + return Number(blockTime); +} From e9a569e0f36886bd82079e815c9de0cd0d31039c Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 9 Sep 2024 12:33:04 -0500 Subject: [PATCH 02/20] docs(best-practices): move out of guides (#3157) --- docs/next.config.mjs | 20 +++++++++++++++++++ docs/pages/_meta.js | 1 + .../{guides => }/best-practices/_meta.js | 4 ++-- .../kms.mdx => best-practices/aws-kms.mdx} | 2 -- .../{guides => }/best-practices/batchCall.svg | 0 .../{guides => }/best-practices/callFrom.svg | 0 .../best-practices/deployment-settings.mdx | 0 .../best-practices/dividing-into-systems.mdx | 0 .../{guides => }/best-practices/libraries.svg | 0 .../best-practices/no-context.svg | 0 .../system.mdx} | 0 docs/pages/guides/_meta.js | 1 - 12 files changed, 23 insertions(+), 5 deletions(-) rename docs/pages/{guides => }/best-practices/_meta.js (57%) rename docs/pages/{guides/best-practices/kms.mdx => best-practices/aws-kms.mdx} (99%) rename docs/pages/{guides => }/best-practices/batchCall.svg (100%) rename docs/pages/{guides => }/best-practices/callFrom.svg (100%) rename docs/pages/{guides => }/best-practices/deployment-settings.mdx (100%) rename docs/pages/{guides => }/best-practices/dividing-into-systems.mdx (100%) rename docs/pages/{guides => }/best-practices/libraries.svg (100%) rename docs/pages/{guides => }/best-practices/no-context.svg (100%) rename docs/pages/{guides/best-practices/system-best-practices.mdx => best-practices/system.mdx} (100%) diff --git a/docs/next.config.mjs b/docs/next.config.mjs index 2a17180f22..67b0cdaf13 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -256,6 +256,26 @@ export default withNextra({ destination: "/store/store-hooks", permanent: false, }, + { + source: "/guides/best-practices/dividing-into-systems", + destination: "/best-practices/dividing-into-systems", + permanent: false, + }, + { + source: "/guides/best-practices/system-best-practices", + destination: "/best-practices/system", + permanent: false, + }, + { + source: "/guides/best-practices/deployment-settings", + destination: "/best-practices/deployment-settings", + permanent: false, + }, + { + source: "/guides/best-practices/kms", + destination: "/best-practices/aws-kms", + permanent: false, + }, ]; }, }); diff --git a/docs/pages/_meta.js b/docs/pages/_meta.js index 73f48ab647..139460a64a 100644 --- a/docs/pages/_meta.js +++ b/docs/pages/_meta.js @@ -33,6 +33,7 @@ export default { }, guides: "Guides", templates: "Templates", + "best-practices": "Best Practices", contribute: { title: "Contribute", theme: { breadcrumb: false }, diff --git a/docs/pages/guides/best-practices/_meta.js b/docs/pages/best-practices/_meta.js similarity index 57% rename from docs/pages/guides/best-practices/_meta.js rename to docs/pages/best-practices/_meta.js index 8469a9e745..d21ab54919 100644 --- a/docs/pages/guides/best-practices/_meta.js +++ b/docs/pages/best-practices/_meta.js @@ -1,7 +1,7 @@ export default { "dividing-into-systems": "Dividing Code into Systems", - "system-best-practices": "System Best Practices", + "system": "System Best Practices", "deployment-settings": "Recommended Deployment Settings", - "kms": "Deploy production worlds using AWS KMS" + "aws-kms": "Deploy production worlds using AWS KMS" }; diff --git a/docs/pages/guides/best-practices/kms.mdx b/docs/pages/best-practices/aws-kms.mdx similarity index 99% rename from docs/pages/guides/best-practices/kms.mdx rename to docs/pages/best-practices/aws-kms.mdx index 4c1f9292c6..01d870babd 100644 --- a/docs/pages/guides/best-practices/kms.mdx +++ b/docs/pages/best-practices/aws-kms.mdx @@ -1,5 +1,3 @@ -import { Callout } from "nextra/components"; - # Deploy production worlds using AWS KMS The MUD equivalent of [root](https://en.wikipedia.org/wiki/Superuser) is the owner of the root namespace, by default this is the address that deployed the `World`. diff --git a/docs/pages/guides/best-practices/batchCall.svg b/docs/pages/best-practices/batchCall.svg similarity index 100% rename from docs/pages/guides/best-practices/batchCall.svg rename to docs/pages/best-practices/batchCall.svg diff --git a/docs/pages/guides/best-practices/callFrom.svg b/docs/pages/best-practices/callFrom.svg similarity index 100% rename from docs/pages/guides/best-practices/callFrom.svg rename to docs/pages/best-practices/callFrom.svg diff --git a/docs/pages/guides/best-practices/deployment-settings.mdx b/docs/pages/best-practices/deployment-settings.mdx similarity index 100% rename from docs/pages/guides/best-practices/deployment-settings.mdx rename to docs/pages/best-practices/deployment-settings.mdx diff --git a/docs/pages/guides/best-practices/dividing-into-systems.mdx b/docs/pages/best-practices/dividing-into-systems.mdx similarity index 100% rename from docs/pages/guides/best-practices/dividing-into-systems.mdx rename to docs/pages/best-practices/dividing-into-systems.mdx diff --git a/docs/pages/guides/best-practices/libraries.svg b/docs/pages/best-practices/libraries.svg similarity index 100% rename from docs/pages/guides/best-practices/libraries.svg rename to docs/pages/best-practices/libraries.svg diff --git a/docs/pages/guides/best-practices/no-context.svg b/docs/pages/best-practices/no-context.svg similarity index 100% rename from docs/pages/guides/best-practices/no-context.svg rename to docs/pages/best-practices/no-context.svg diff --git a/docs/pages/guides/best-practices/system-best-practices.mdx b/docs/pages/best-practices/system.mdx similarity index 100% rename from docs/pages/guides/best-practices/system-best-practices.mdx rename to docs/pages/best-practices/system.mdx diff --git a/docs/pages/guides/_meta.js b/docs/pages/guides/_meta.js index 7629620ff6..70fcd21bcc 100644 --- a/docs/pages/guides/_meta.js +++ b/docs/pages/guides/_meta.js @@ -6,5 +6,4 @@ export default { "modules": "Writing MUD Modules", emojimon: "Emojimon", testing: "Testing", - "best-practices": "Best Practices", }; From 597bc0fe4221620c11b8685a24f8ecd848343350 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 9 Sep 2024 12:33:21 -0500 Subject: [PATCH 03/20] =?UTF-8?q?docs(top=20bar):=20remove=20status=20?= =?UTF-8?q?=F0=9F=9A=97=20=20(#3158)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/pages/_meta.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/pages/_meta.js b/docs/pages/_meta.js index 139460a64a..b91748e91c 100644 --- a/docs/pages/_meta.js +++ b/docs/pages/_meta.js @@ -56,12 +56,6 @@ export default { }, }, }, - status: { - title: "Status", - type: "page", - href: "https://status.mud.dev", - newWindow: true, - }, community: { title: "Community", type: "page", From 12a12ca5852b4e4434774c1a00e58e1662462566 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 9 Sep 2024 12:34:20 -0500 Subject: [PATCH 04/20] =?UTF-8?q?docs(world/modules/erc721):=20change=20`D?= =?UTF-8?q?DDD`=20to=20`MyToken`=20=F0=9F=9A=97=20=20(#3159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/pages/world/modules/erc721.mdx | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/pages/world/modules/erc721.mdx b/docs/pages/world/modules/erc721.mdx index 0fd447899a..6696c52ac2 100644 --- a/docs/pages/world/modules/erc721.mdx +++ b/docs/pages/world/modules/erc721.mdx @@ -15,12 +15,15 @@ The advantage of doing this, rather than creating a separate [NFT contract](http ## Deployment The easiest way to deploy this module is to edit `mud.config.ts`. +This is a modified version of the [vanilla](/templates/typescript/contracts) template. + +Note that before you use this file you need to run `pnpm add viem` (see explanation below). -```typescript filename="mud.config.ts" showLineNumbers copy {2-16,28-44} +```typescript filename="mud.config.ts" showLineNumbers copy {2-13,25-41} import { defineWorld } from "@latticexyz/world"; -import { encodeAbiParameters } from "viem"; +import { encodeAbiParameters, stringToHex } from "viem"; const erc721ModuleArgs = encodeAbiParameters( [ @@ -30,7 +33,7 @@ const erc721ModuleArgs = encodeAbiParameters( components: [{ type: "string" }, { type: "string" }, { type: "string" }], }, ], - ["0x44444444".padEnd(30, "0"), ["No Valuable Token", "NVT", "http://www.example.com/base/uri/goes/here"]], + [stringToHex("MyNFT", { size: 14 }), ["No Valuable Token", "NVT", "http://www.example.com/base/uri/goes/here"]], ); export default defineWorld({ @@ -70,14 +73,15 @@ export default defineWorld({ Explanation ```typescript -import { encodeAbiParameters } from "viem"; +import { encodeAbiParameters, stringToHex } from "viem"; ``` In simple cases it is enough to use the config parser to specify the module arguments. However, the NFT module requires a `struct` as [one of the arguments](https://github.com/latticexyz/mud/blob/main/packages/world-modules/src/modules/erc721-puppet/ERC721Module.sol#L37). We use [`encodeAbiParameters`](https://viem.sh/docs/abi/encodeAbiParameters.html) to encode the `struct` data. +The [`stringToHex`](https://viem.sh/docs/utilities/toHex.html#stringtohex) function is used to specify the namespace the token uses. -Note that this means we need to issue `pnpm install viem` in `packages/contracts` to be able to use the library here. +This is the reason we need to issue `pnpm install viem` in `packages/contracts` to be able to use the library here. ```typescript const erc721ModuleArgs = encodeAbiParameters( @@ -113,14 +117,13 @@ It consists of three strings (the token name, symbol, and [base URI](https://doc ```typescript [ - "0x44444444".padEnd(30, "0"), + stringToHex("MyNFT", { size: 14 }), ``` The second `encodeAbiParameters` parameter is a list of the values, of the types declared in the first list. -The first parameter for the module is `bytes14`, which is expected to be a hexadecimal value with twenty-eight hexadecimal digits, for a total length of thirty. -Here we use `0x4...40....0` with eight 4's followed by twenty 0's. -This gives us the namespace `DDDD`, which is easy to recognize both as hex and as text. +The first parameter for the module is `bytes14`, the namespace of the ERC-721 token. +We use [`stringToHex`](https://viem.sh/docs/utilities/toHex.html#stringtohex) to convert it from the text form that is easy for us to use, to the hexadecimal number that Viem expects for `bytes14` parameter. ```typescript ["No Valuable Token", "NVT", "http://www.example.com/base/uri/goes/here"], @@ -180,7 +183,7 @@ For example, run this script. -```solidity filename="ManageERC721.s.sol" copy {29-34, 42-58} +```solidity filename="ManageERC721.s.sol" copy {29-33, 41-57} // SPDX-License-Identifier: MIT pragma solidity >=0.8.24; @@ -210,7 +213,7 @@ contract ManageERC721 is Script { vm.startBroadcast(deployerPrivateKey); // Get the ERC-721 token address - ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("DDDD")); + ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("MyNFT")); ResourceId erc721RegistryResource = WorldResourceIdLib.encode(RESOURCE_TABLE, "erc721-puppet", "ERC721Registry"); address tokenAddress = ERC721Registry.getTokenAddress(erc721RegistryResource, namespaceResource); console.log("Token address", tokenAddress); @@ -254,7 +257,7 @@ contract ManageERC721 is Script { ```solidity // Get the ERC-721 token address - ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("DDDD")); + ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("MyNFT")); ResourceId erc721RegistryResource = WorldResourceIdLib.encode(RESOURCE_TABLE, "erc721-puppet", "ERC721Registry"); address tokenAddress = ERC721Registry.getTokenAddress(erc721RegistryResource, namespaceResource); @@ -263,7 +266,7 @@ contract ManageERC721 is Script { This is the process to get the address of our token contract (the puppet). First, we get the [`resourceId` values](/world/resource-ids) for the `erc721-puppet__ERC721Registry` table and the namespace we are interested in (each namespace can only have one ERC721 token). -Then we use `ERC721Registry` to get the token address. +Then we use that table to get the token address. ```solidity // Use the token From 1391018e6009c19a6dcd799cc3e6b807696cfe4a Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Mon, 9 Sep 2024 17:41:43 -0500 Subject: [PATCH 05/20] =?UTF-8?q?docs(indexer):=20move=20out=20of=20servic?= =?UTF-8?q?es,=20remove=20the=20section,=20move=20faucet=20to=20README.md?= =?UTF-8?q?=20=F0=9F=9A=97=20=20(#3160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/next.config.mjs | 27 +++++++--- docs/pages/_meta.js | 2 +- docs/pages/{services => }/indexer.mdx | 2 +- docs/pages/{services => }/indexer/_meta.js | 4 +- .../indexer/postgres-decoded.mdx | 2 +- .../indexer/postgres-event-only.mdx | 2 +- .../sqlite-indexer.mdx => indexer/sqlite.mdx} | 2 +- .../using-indexer.mdx => indexer/using.mdx} | 2 +- docs/pages/services/faucet.mdx | 45 ---------------- packages/faucet/README.md | 53 ++++++++++++------- 10 files changed, 62 insertions(+), 79 deletions(-) rename docs/pages/{services => }/indexer.mdx (97%) rename docs/pages/{services => }/indexer/_meta.js (62%) rename docs/pages/{services => }/indexer/postgres-decoded.mdx (99%) rename docs/pages/{services => }/indexer/postgres-event-only.mdx (99%) rename docs/pages/{services/indexer/sqlite-indexer.mdx => indexer/sqlite.mdx} (99%) rename docs/pages/{services/indexer/using-indexer.mdx => indexer/using.mdx} (96%) delete mode 100644 docs/pages/services/faucet.mdx diff --git a/docs/next.config.mjs b/docs/next.config.mjs index 67b0cdaf13..20c48cdbf8 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -116,11 +116,6 @@ export default withNextra({ destination: "/state-query/typescript/recs", permanent: false, }, - { - source: "/indexer", - destination: "/services/indexer", - permanent: false, - }, { source: "/cli", destination: "/cli/tablegen", @@ -275,7 +270,27 @@ export default withNextra({ source: "/guides/best-practices/kms", destination: "/best-practices/aws-kms", permanent: false, - }, + }, + { + source: "/services/indexer/postgres-decoded", + destination: "/indexer/postgres-decoded", + permanent: false, + }, + { + source: "/services/indexer/postgres-event-only", + destination: "/indexer/postgres-event-only", + permanent: false, + }, + { + source: "/services/indexer/sqlite-indexer", + destination: "/indexer/sqlite", + permanent: false, + }, + { + source: "/services/indexer/using-indexer", + destination: "/indexer/using", + permanent: false, + }, ]; }, }); diff --git a/docs/pages/_meta.js b/docs/pages/_meta.js index b91748e91c..e4d2a62807 100644 --- a/docs/pages/_meta.js +++ b/docs/pages/_meta.js @@ -22,7 +22,7 @@ export default { config: "Config", cli: "CLI", "state-query": "State Query", - services: "Services", + indexer: "Indexer", "world-explorer": { title: "World Explorer", theme: { breadcrumb: false }, diff --git a/docs/pages/services/indexer.mdx b/docs/pages/indexer.mdx similarity index 97% rename from docs/pages/services/indexer.mdx rename to docs/pages/indexer.mdx index f6079f6e7d..61c6ec0235 100644 --- a/docs/pages/services/indexer.mdx +++ b/docs/pages/indexer.mdx @@ -1,5 +1,5 @@ import { Callout } from "nextra/components"; -import { CollapseCode } from "../../components/CollapseCode"; +import { CollapseCode } from "../components/CollapseCode"; # Indexer diff --git a/docs/pages/services/indexer/_meta.js b/docs/pages/indexer/_meta.js similarity index 62% rename from docs/pages/services/indexer/_meta.js rename to docs/pages/indexer/_meta.js index 3def627a40..9e27e90228 100644 --- a/docs/pages/services/indexer/_meta.js +++ b/docs/pages/indexer/_meta.js @@ -1,6 +1,6 @@ export default { - "using-indexer": "Using the Indexer", - "sqlite-indexer": "SQLite Indexer", + "using": "Using the Indexer", + "sqlite": "SQLite Indexer", "postgres-event-only": "PostgreSQL for events", "postgres-decoded": "PostgreSQL for data (and events)", }; diff --git a/docs/pages/services/indexer/postgres-decoded.mdx b/docs/pages/indexer/postgres-decoded.mdx similarity index 99% rename from docs/pages/services/indexer/postgres-decoded.mdx rename to docs/pages/indexer/postgres-decoded.mdx index 8c4dae25c9..eff36a5074 100644 --- a/docs/pages/services/indexer/postgres-decoded.mdx +++ b/docs/pages/indexer/postgres-decoded.mdx @@ -1,5 +1,5 @@ import { Callout } from "nextra/components"; -import { CollapseCode } from "../../../components/CollapseCode"; +import { CollapseCode } from "../../components/CollapseCode"; # Postgres for Data and Events diff --git a/docs/pages/services/indexer/postgres-event-only.mdx b/docs/pages/indexer/postgres-event-only.mdx similarity index 99% rename from docs/pages/services/indexer/postgres-event-only.mdx rename to docs/pages/indexer/postgres-event-only.mdx index cc4efa7625..d9db10d6a3 100644 --- a/docs/pages/services/indexer/postgres-event-only.mdx +++ b/docs/pages/indexer/postgres-event-only.mdx @@ -1,5 +1,5 @@ import { Callout } from "nextra/components"; -import { CollapseCode } from "../../../components/CollapseCode"; +import { CollapseCode } from "../../components/CollapseCode"; # Postgres for Events Only diff --git a/docs/pages/services/indexer/sqlite-indexer.mdx b/docs/pages/indexer/sqlite.mdx similarity index 99% rename from docs/pages/services/indexer/sqlite-indexer.mdx rename to docs/pages/indexer/sqlite.mdx index 9d3d5bdccd..56168b6133 100644 --- a/docs/pages/services/indexer/sqlite-indexer.mdx +++ b/docs/pages/indexer/sqlite.mdx @@ -1,5 +1,5 @@ import { Callout } from "nextra/components"; -import { CollapseCode } from "../../../components/CollapseCode"; +import { CollapseCode } from "../../components/CollapseCode"; # SQLite indexer diff --git a/docs/pages/services/indexer/using-indexer.mdx b/docs/pages/indexer/using.mdx similarity index 96% rename from docs/pages/services/indexer/using-indexer.mdx rename to docs/pages/indexer/using.mdx index 1e56f26b83..2ac46ff64e 100644 --- a/docs/pages/services/indexer/using-indexer.mdx +++ b/docs/pages/indexer/using.mdx @@ -1,5 +1,5 @@ import { Callout } from "nextra/components"; -import { CollapseCode } from "../../../components/CollapseCode"; +import { CollapseCode } from "../../components/CollapseCode"; # Using the indexer diff --git a/docs/pages/services/faucet.mdx b/docs/pages/services/faucet.mdx deleted file mode 100644 index 9e691a003a..0000000000 --- a/docs/pages/services/faucet.mdx +++ /dev/null @@ -1,45 +0,0 @@ -# Faucet - -A minimal Typescript faucet to drip native tokens on Ethereum chains - -## Usage - -1. Specify the environment variables. - Note that you need to [`export`](https://linuxconfig.org/export) these variables for the faucet service to get them. - - | Variable | Description | Default | - | -------------------------- | ----------------------------------------------------- | --------- | - | `HOST` | Host that the indexer server listens on | `0.0.0.0` | - | `PORT` | Port that the indexer server listens on | `3002` | - | `RPC_HTTP_URL`1 | HTTP URL for Ethereum RPC | | - | `FAUCET_PRIVATE_KEY` | Private key of wallet to distribute faucet funds from | | - | `DRIP_AMOUNT_ETHER` | Drip amount in ether | | - - (1) If you use `http://localhost:8545` and it fails try using `http://127.0.0.1:8545` instead - -1. Start the server. - - ```sh copy - npx @latticexyz/faucet - ``` - - For example, you can use these values to drip 1 ETH for a local [`anvil`](https://book.getfoundry.sh/anvil/) instance: - - | Variable | Value | Comments | - | -------------------- | ------------------------------------------------------------------ | ---------------------------------- | - | `RPC_HTTP_URL` | http://127.0.0.1:8545 | `localhost` sometimes doesn't work | - | `FAUCET_PRIVATE_KEY` | 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d | One of the default accounts | - | `DRIP_AMOUNT_ETHER` | 1 | - -## Getting ETH from a faucet - -[This sample program](https://github.com/latticexyz/mud/tree/main/examples/faucet-client) shows how to obtain ETH from a faucet using TypeScript. - -Alternatively, you can submit a `POST` request to the faucet. -For example, if you use the default `HOST` and `PORT`, this command requests a drip to the zero address. - -```sh copy -curl -X POST http://127.0.0.1:3002/trpc/drip \ - -H "Content-Type: application/json" \ - -d '{"address": "0x0000000000000000000000000000000000000000"}' -``` diff --git a/packages/faucet/README.md b/packages/faucet/README.md index 6afc0f84ff..dea4320af8 100644 --- a/packages/faucet/README.md +++ b/packages/faucet/README.md @@ -1,30 +1,43 @@ -# faucet - A minimal Typescript faucet to drip native tokens on Ethereum chains ## Usage -Install and run with: +1. Specify the environment variables. + Note that you need to [`export`](https://linuxconfig.org/export) these variables for the faucet service to get them. -```sh -npm install @latticexyz/faucet@next -npm faucet-server -``` + | Variable | Description | Default | + | -------------------------- | ----------------------------------------------------- | --------- | + | `HOST` | Host that the indexer server listens on | `0.0.0.0` | + | `PORT` | Port that the indexer server listens on | `3002` | + | `RPC_HTTP_URL`1 | HTTP URL for Ethereum RPC | | + | `FAUCET_PRIVATE_KEY` | Private key of wallet to distribute faucet funds from | | + | `DRIP_AMOUNT_ETHER` | Drip amount in ether | | -or execute the package bin directly: + (1) If you use `http://localhost:8545` and it fails try using `http://127.0.0.1:8545` instead -```sh -npx @latticexyz/faucet@next -``` +1. Start the server. + + ```sh copy + npx @latticexyz/faucet + ``` -## Configuration + For example, you can use these values to drip 1 ETH for a local [`anvil`](https://book.getfoundry.sh/anvil/) instance: -The faucet can configured with the following environment variables: + | Variable | Value | Comments | + | -------------------- | ------------------------------------------------------------------ | ---------------------------------- | + | `RPC_HTTP_URL` | http://127.0.0.1:8545 | `localhost` sometimes doesn't work | + | `FAUCET_PRIVATE_KEY` | 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d | One of the default accounts | + | `DRIP_AMOUNT_ETHER` | 1 | -| Variable | Description | Default | -| -------------------- | ----------------------------------------------------- | --------- | -| `HOST` | Host that the indexer server listens on | `0.0.0.0` | -| `PORT` | Port that the indexer server listens on | `3002` | -| `RPC_HTTP_URL` | HTTP URL for Ethereum RPC | | -| `FAUCET_PRIVATE_KEY` | Private key of wallet to distribute faucet funds from | | -| `DRIP_AMOUNT_ETHER` | Drip amount in ether | +## Getting ETH from a faucet + +[This sample program](https://github.com/latticexyz/mud/tree/main/examples/faucet-client) shows how to obtain ETH from a faucet using TypeScript. + +Alternatively, you can submit a `POST` request to the faucet. +For example, if you use the default `HOST` and `PORT`, this command requests a drip to the zero address. + +```sh copy +curl -X POST http://127.0.0.1:3002/trpc/drip \ + -H "Content-Type: application/json" \ + -d '{"address": "0x0000000000000000000000000000000000000000"}' +``` From ee4bcc456ff15b1f4830953525880b898fef5afe Mon Sep 17 00:00:00 2001 From: Karolis Ramanauskas Date: Tue, 10 Sep 2024 17:27:10 +0300 Subject: [PATCH 06/20] feat(explorer): external wallet connect (#3156) --- packages/explorer/package.json | 1 + .../explorer/src/app/(explorer)/Providers.tsx | 16 ++- pnpm-lock.yaml | 118 +++++++++++++++++- 3 files changed, 126 insertions(+), 9 deletions(-) diff --git a/packages/explorer/package.json b/packages/explorer/package.json index d1e4f55dce..5bed4e5d84 100644 --- a/packages/explorer/package.json +++ b/packages/explorer/package.json @@ -37,6 +37,7 @@ "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", "@radix-ui/themes": "^3.0.5", + "@rainbow-me/rainbowkit": "^2.1.5", "@tanstack/react-query": "^5.51.3", "@tanstack/react-table": "^8.19.3", "@wagmi/core": "^2.12.1", diff --git a/packages/explorer/src/app/(explorer)/Providers.tsx b/packages/explorer/src/app/(explorer)/Providers.tsx index cf3ea465e4..1118b046c0 100644 --- a/packages/explorer/src/app/(explorer)/Providers.tsx +++ b/packages/explorer/src/app/(explorer)/Providers.tsx @@ -1,17 +1,18 @@ "use client"; -import { WagmiProvider } from "wagmi"; +import { WagmiProvider, createConfig, http } from "wagmi"; import { injected, metaMask, safe } from "wagmi/connectors"; import { ReactNode } from "react"; +import { RainbowKitProvider, darkTheme } from "@rainbow-me/rainbowkit"; +import "@rainbow-me/rainbowkit/styles.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { createConfig, http } from "@wagmi/core"; -import { localhost } from "@wagmi/core/chains"; +import { anvil } from "@wagmi/core/chains"; import { AppStoreProvider } from "../../store"; const queryClient = new QueryClient(); export const wagmiConfig = createConfig({ - chains: [localhost], + chains: [anvil], connectors: [ injected(), metaMask({ @@ -22,15 +23,18 @@ export const wagmiConfig = createConfig({ safe(), ], transports: { - [localhost.id]: http(), + [anvil.id]: http(), }, + ssr: true, }); export function Providers({ children }: { children: ReactNode }) { return ( - {children} + + {children} + ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da5e3efbbf..7abba8e677 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -497,6 +497,9 @@ importers: '@radix-ui/themes': specifier: ^3.0.5 version: 3.1.3(@types/react-dom@18.2.7)(@types/react@18.2.22)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@rainbow-me/rainbowkit': + specifier: ^2.1.5 + version: 2.1.5(@tanstack/react-query@5.52.0(react@18.2.0))(@types/react@18.2.22)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(viem@2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.0(react@18.2.0))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.75.2(@babel/core@7.21.4)(@babel/preset-env@7.25.3(@babel/core@7.21.4))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@3.21.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) '@tanstack/react-query': specifier: ^5.51.3 version: 5.52.0(react@18.2.0) @@ -2489,6 +2492,9 @@ packages: '@coinbase/wallet-sdk@4.0.4': resolution: {integrity: sha512-74c040CRnGhfRjr3ArnkAgud86erIqdkPHNt5HR1k9u97uTIZCJww9eGYT67Qf7gHPpGS/xW8Be1D4dvRm63FA==} + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + '@esbuild-kit/cjs-loader@2.4.2': resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} @@ -4089,6 +4095,16 @@ packages: '@types/react-dom': optional: true + '@rainbow-me/rainbowkit@2.1.5': + resolution: {integrity: sha512-Kdef0zu0bUlIOlbyyi3ukmQl7k8s3w0jTcWZxYTicZ/N4L35yX0vEzYgiG4u6OSXlbAQaC7VrkPKugPbSohnLQ==} + engines: {node: '>=12.4'} + peerDependencies: + '@tanstack/react-query': '>=5.0.0' + react: '>=18' + react-dom: '>=18' + viem: 2.x + wagmi: ^2.9.0 + '@react-native-community/cli-clean@14.0.0': resolution: {integrity: sha512-kvHthZTNur/wLLx8WL5Oh+r04zzzFAX16r8xuaLhu9qGTE6Th1JevbsIuiQb5IJqD8G/uZDKgIZ2a0/lONcbJg==} @@ -4866,6 +4882,20 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@vanilla-extract/css@1.14.0': + resolution: {integrity: sha512-rYfm7JciWZ8PFzBM/HDiE2GLnKI3xJ6/vdmVJ5BSgcCZ5CxRlM9Cjqclni9lGzF3eMOijnUhCd/KV8TOzyzbMA==} + + '@vanilla-extract/dynamic@2.1.0': + resolution: {integrity: sha512-8zl0IgBYRtgD1h+56Zu13wHTiMTJSVEa4F7RWX9vTB/5Xe2KtjoiqApy/szHPVFA56c+ex6A4GpCQjT1bKXbYw==} + + '@vanilla-extract/private@1.0.6': + resolution: {integrity: sha512-ytsG/JLweEjw7DBuZ/0JCN4WAQgM9erfSTdS1NQY778hFQSZ6cfCDEZZ0sgVm4k54uNz6ImKB33AYvSR//fjxw==} + + '@vanilla-extract/sprinkles@1.6.1': + resolution: {integrity: sha512-N/RGKwGAAidBupZ436RpuweRQHEFGU+mvAqBo8PRMAjJEmHoPDttV8RObaMLrJHWLqvX+XUMinHUnD0hFRQISw==} + peerDependencies: + '@vanilla-extract/css': ^1.0.0 + '@viem/anvil@0.0.7': resolution: {integrity: sha512-F+3ljCT1bEt8T4Fzm9gWpIgO3Dc7bzG1TtUtkStkJFMuummqZ8kvYc3UFMo5j3F51fSWZZvEkjs3+i7qf0AOqQ==} @@ -5679,6 +5709,10 @@ packages: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} + clsx@2.1.0: + resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} + engines: {node: '>=6'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -5859,6 +5893,10 @@ packages: uWebSockets.js: optional: true + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -5968,6 +6006,9 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deep-object-diff@1.1.9: + resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -8038,6 +8079,9 @@ packages: marky@1.2.5: resolution: {integrity: sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==} + media-query-parser@2.0.2: + resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -8256,6 +8300,9 @@ packages: mobx@6.9.0: resolution: {integrity: sha512-HdKewQEREEJgsWnErClfbFoVebze6rGazxFLU/XUyrII8dORfVszN1V0BMRnQSzcgsNNtkX8DHj3nC6cdWE9YQ==} + modern-ahocorasick@1.0.1: + resolution: {integrity: sha512-yoe+JbhTClckZ67b2itRtistFKf8yPYelHLc7e5xAwtNAXxM6wJTUx2C7QeVSJFDzKT7bCIFyBVybPMKvmB9AA==} + motion@10.16.2: resolution: {integrity: sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ==} @@ -8589,6 +8636,9 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + outdent@0.8.0: + resolution: {integrity: sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==} + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -10268,6 +10318,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ua-parser-js@1.0.38: + resolution: {integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==} + ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} @@ -12547,6 +12600,8 @@ snapshots: preact: 10.23.2 sha.js: 2.4.11 + '@emotion/hash@0.9.2': {} + '@esbuild-kit/cjs-loader@2.4.2': dependencies: '@esbuild-kit/core-utils': 3.1.0 @@ -14221,6 +14276,23 @@ snapshots: '@types/react': 18.2.22 '@types/react-dom': 18.2.7 + '@rainbow-me/rainbowkit@2.1.5(@tanstack/react-query@5.52.0(react@18.2.0))(@types/react@18.2.22)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(viem@2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.0(react@18.2.0))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.75.2(@babel/core@7.21.4)(@babel/preset-env@7.25.3(@babel/core@7.21.4))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@3.21.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))': + dependencies: + '@tanstack/react-query': 5.52.0(react@18.2.0) + '@vanilla-extract/css': 1.14.0 + '@vanilla-extract/dynamic': 2.1.0 + '@vanilla-extract/sprinkles': 1.6.1(@vanilla-extract/css@1.14.0) + clsx: 2.1.0 + qrcode: 1.5.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.7(@types/react@18.2.22)(react@18.2.0) + ua-parser-js: 1.0.38 + viem: 2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8) + wagmi: 2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.0(react@18.2.0))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.75.2(@babel/core@7.21.4)(@babel/preset-env@7.25.3(@babel/core@7.21.4))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@3.21.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + transitivePeerDependencies: + - '@types/react' + '@react-native-community/cli-clean@14.0.0': dependencies: '@react-native-community/cli-tools': 14.0.0 @@ -15422,6 +15494,30 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + '@vanilla-extract/css@1.14.0': + dependencies: + '@emotion/hash': 0.9.2 + '@vanilla-extract/private': 1.0.6 + chalk: 4.1.2 + css-what: 6.1.0 + cssesc: 3.0.0 + csstype: 3.1.2 + deep-object-diff: 1.1.9 + deepmerge: 4.3.1 + media-query-parser: 2.0.2 + modern-ahocorasick: 1.0.1 + outdent: 0.8.0 + + '@vanilla-extract/dynamic@2.1.0': + dependencies: + '@vanilla-extract/private': 1.0.6 + + '@vanilla-extract/private@1.0.6': {} + + '@vanilla-extract/sprinkles@1.6.1(@vanilla-extract/css@1.14.0)': + dependencies: + '@vanilla-extract/css': 1.14.0 + '@viem/anvil@0.0.7(bufferutil@4.0.8)(debug@4.3.4)(utf-8-validate@5.0.10)': dependencies: execa: 7.2.0 @@ -16619,6 +16715,8 @@ snapshots: clsx@2.0.0: {} + clsx@2.1.0: {} + clsx@2.1.1: {} co@4.6.0: {} @@ -16813,6 +16911,8 @@ snapshots: crossws@0.2.4: {} + css-what@6.1.0: {} + cssesc@3.0.0: {} cssstyle@3.0.0: @@ -16856,7 +16956,7 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.25.0 dayjs@1.11.13: {} @@ -16917,6 +17017,8 @@ snapshots: deep-is@0.1.4: {} + deep-object-diff@1.1.9: {} + deepmerge@4.3.1: {} defaults@1.0.4: @@ -18398,7 +18500,7 @@ snapshots: i18next-browser-languagedetector@7.1.0: dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.25.0 i18next@23.11.5: dependencies: @@ -19695,6 +19797,10 @@ snapshots: marky@1.2.5: {} + media-query-parser@2.0.2: + dependencies: + '@babel/runtime': 7.25.0 + media-typer@0.3.0: {} memoize-one@5.2.1: {} @@ -20011,6 +20117,8 @@ snapshots: mobx@6.9.0: {} + modern-ahocorasick@1.0.1: {} + motion@10.16.2: dependencies: '@motionone/animation': 10.18.0 @@ -20263,7 +20371,7 @@ snapshots: objnest@5.1.1: dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.25.0 abind: 1.0.5 extend: 3.0.2 @@ -20360,6 +20468,8 @@ snapshots: outdent@0.5.0: {} + outdent@0.8.0: {} + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -22165,6 +22275,8 @@ snapshots: typescript@5.4.2: {} + ua-parser-js@1.0.38: {} + ufo@1.5.4: {} uglify-js@3.17.4: From b9c61a96082e62c4f1bec3a8ebb358ea30c315f0 Mon Sep 17 00:00:00 2001 From: Karolis Ramanauskas Date: Tue, 10 Sep 2024 19:15:10 +0300 Subject: [PATCH 07/20] fix(explorer): world address cli option as hex (#3155) Co-authored-by: Kevin Ingersoll --- .changeset/itchy-trees-retire.md | 5 + package.json | 5 - packages/explorer/README.md | 1 + packages/explorer/bin/explorer.ts | 99 ++++++++---- packages/explorer/package.json | 3 +- patches/minimist@1.2.8-allow-hex-values.patch | 12 -- pnpm-lock.yaml | 146 +++++------------- 7 files changed, 123 insertions(+), 148 deletions(-) create mode 100644 .changeset/itchy-trees-retire.md delete mode 100644 patches/minimist@1.2.8-allow-hex-values.patch diff --git a/.changeset/itchy-trees-retire.md b/.changeset/itchy-trees-retire.md new file mode 100644 index 0000000000..28d31b7d7a --- /dev/null +++ b/.changeset/itchy-trees-retire.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/explorer": patch +--- + +Fixed an issue with `--worldAddress` CLI flag being incorrectly interpreted as a number rather a hex string. Additionally, added `--hostname` option for specifying the hostname on which to start the application. diff --git a/package.json b/package.json index 7af5ad1c23..ff3e249430 100644 --- a/package.json +++ b/package.json @@ -58,10 +58,5 @@ "engines": { "node": "^18.20.1", "pnpm": "^9.6.0" - }, - "pnpm": { - "patchedDependencies": { - "minimist@1.2.8": "patches/minimist@1.2.8-allow-hex-values.patch" - } } } diff --git a/packages/explorer/README.md b/packages/explorer/README.md index 00908ebdf4..fa5377abe4 100644 --- a/packages/explorer/README.md +++ b/packages/explorer/README.md @@ -45,6 +45,7 @@ The World Explorer accepts the following CLI arguments: | `indexerDatabase` | Path to your SQLite indexer database | "indexer.db" | | `chainId` | The chain ID of the network | 31337 | | `port` | The port on which to run the World Explorer | 13690 | +| `hostname` | The host on which to run the World Explorer | 0.0.0.0 | | `dev` | Run the World Explorer in development mode | false | ## Contributing diff --git a/packages/explorer/bin/explorer.ts b/packages/explorer/bin/explorer.ts index 13ff25ec10..e7b3bcc06f 100755 --- a/packages/explorer/bin/explorer.ts +++ b/packages/explorer/bin/explorer.ts @@ -1,46 +1,93 @@ #!/usr/bin/env node import { watchFile } from "fs"; import { readFile } from "fs/promises"; -import minimist from "minimist"; import path from "path"; import process from "process"; import { fileURLToPath } from "url"; +import yargs from "yargs"; import { ChildProcess, spawn } from "child_process"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const argv = minimist(process.argv.slice(2)); -const port = argv.port || process.env.PORT || 13690; -const chainId = argv.chainId || process.env.CHAIN_ID || 31337; -const indexerDatabase = argv.indexerDatabase || process.env.INDEXER_DATABASE || "indexer.db"; -const worldsFile = argv.worldsFile || process.env.WORLDS_FILE || "worlds.json"; -const isDev = !!argv.dev; +const argv = yargs(process.argv.slice(2)) + .options({ + port: { + alias: "p", + description: "Port number for the server", + type: "number", + default: process.env.PORT || 13690, + }, + hostname: { + alias: "H", + description: "Host for the server", + type: "string", + }, + chainId: { + alias: "c", + description: "Chain ID", + type: "number", + default: process.env.CHAIN_ID || 31337, + }, + indexerDatabase: { + alias: "i", + description: "Path to the indexer database", + type: "string", + default: process.env.INDEXER_DATABASE || "indexer.db", + }, + worldsFile: { + alias: "w", + description: "Path to the worlds.json file", + type: "string", + default: process.env.WORLDS_FILE || "worlds.json", + }, + dev: { + alias: "D", + description: "Run in development mode", + type: "boolean", + default: false, + }, + worldAddress: { + alias: "a", + description: "World address", + type: "string", + default: process.env.WORLD_ADDRESS, + }, + }) + .parseSync(); -let worldAddress = argv.worldAddress || process.env.WORLD_ADDRESS || null; +const { port, hostname, chainId, indexerDatabase, worldsFile, dev } = argv; +let worldAddress = argv.worldAddress; let explorerProcess: ChildProcess; async function startExplorer() { - let command, args; - - if (isDev) { - command = "pnpm"; - args = ["dev"]; + const env = { + ...process.env, + WORLD_ADDRESS: worldAddress?.toString(), + INDEXER_DATABASE: path.join(process.cwd(), indexerDatabase), + }; + + if (dev) { + explorerProcess = spawn( + "node_modules/.bin/next", + ["dev", "--port", port.toString(), ...(hostname ? ["--hostname", hostname] : [])], + { + cwd: path.join(__dirname, ".."), + stdio: "inherit", + env, + }, + ); } else { - command = "pnpm"; - args = ["start"]; + explorerProcess = spawn("node", [".next/standalone/packages/explorer/server.js"], { + cwd: path.join(__dirname, ".."), + stdio: "inherit", + env: { + ...env, + PORT: port.toString(), + HOSTNAME: hostname, + }, + }); } - - explorerProcess = spawn(command, args, { - cwd: __dirname, - stdio: "inherit", - env: { - ...process.env, - PORT: port, - WORLD_ADDRESS: worldAddress, - INDEXER_DATABASE: path.join(process.cwd(), indexerDatabase), - }, - }); } async function readWorldsJson() { diff --git a/packages/explorer/package.json b/packages/explorer/package.json index 5bed4e5d84..f467b3cadb 100644 --- a/packages/explorer/package.json +++ b/packages/explorer/package.json @@ -45,7 +45,6 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.408.0", - "minimist": "^1.2.8", "next": "14.2.5", "query-string": "^9.1.0", "react": "^18", @@ -56,6 +55,7 @@ "tsup": "^6.7.0", "viem": "catalog:", "wagmi": "^2.11.1", + "yargs": "^17.7.1", "zod": "3.23.8", "zustand": "^4.3.7" }, @@ -66,6 +66,7 @@ "@types/node": "^18.15.11", "@types/react": "18.2.22", "@types/react-dom": "18.2.7", + "@types/yargs": "^17.0.10", "eslint-config-next": "14.2.3", "postcss": "^8", "prettier": "3.2.5", diff --git a/patches/minimist@1.2.8-allow-hex-values.patch b/patches/minimist@1.2.8-allow-hex-values.patch deleted file mode 100644 index 208c306e1a..0000000000 --- a/patches/minimist@1.2.8-allow-hex-values.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/index.js b/index.js -index f020f3940e129c361dc89226efaf8775a4af8752..b36b76708f93042f46e7de501d832635016a4931 100644 ---- a/index.js -+++ b/index.js -@@ -12,7 +12,6 @@ function hasKey(obj, keys) { - - function isNumber(x) { - if (typeof x === 'number') { return true; } -- if ((/^0x[0-9a-f]+$/i).test(x)) { return true; } - return (/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/).test(x); - } - diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7abba8e677..24daa0b8d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,11 +22,6 @@ catalogs: specifier: 2.19.8 version: 2.19.8 -patchedDependencies: - minimist@1.2.8: - hash: tkbpkgnnti52zmnvbq3uwanedm - path: patches/minimist@1.2.8-allow-hex-values.patch - importers: .: @@ -521,9 +516,6 @@ importers: lucide-react: specifier: ^0.408.0 version: 0.408.0(react@18.2.0) - minimist: - specifier: ^1.2.8 - version: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) next: specifier: 14.2.5 version: 14.2.5(@babel/core@7.21.4)(@opentelemetry/api@1.8.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -554,6 +546,9 @@ importers: wagmi: specifier: ^2.11.1 version: 2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.0(react@18.2.0))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.75.2(@babel/core@7.21.4)(@babel/preset-env@7.25.3(@babel/core@7.21.4))(@types/react@18.2.22)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(typescript@5.4.2)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@3.21.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(viem@2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + yargs: + specifier: ^17.7.1 + version: 17.7.2 zod: specifier: 3.23.8 version: 3.23.8 @@ -579,6 +574,9 @@ importers: '@types/react-dom': specifier: 18.2.7 version: 18.2.7 + '@types/yargs': + specifier: ^17.0.10 + version: 17.0.24 eslint-config-next: specifier: 14.2.3 version: 14.2.3(eslint@8.57.0)(typescript@5.4.2) @@ -1621,10 +1619,6 @@ packages: resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.21.4': - resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.25.2': resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==} engines: {node: '>=6.9.0'} @@ -1653,12 +1647,6 @@ packages: resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.21.4': - resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-compilation-targets@7.25.2': resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} engines: {node: '>=6.9.0'} @@ -1708,18 +1696,10 @@ packages: resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.18.6': - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.24.7': resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.21.2': - resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.25.2': resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} engines: {node: '>=6.9.0'} @@ -1750,10 +1730,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-simple-access@7.20.2': - resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} - engines: {node: '>=6.9.0'} - '@babel/helper-simple-access@7.24.7': resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} @@ -1786,10 +1762,6 @@ packages: resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.21.0': - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.24.8': resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} engines: {node: '>=6.9.0'} @@ -10926,8 +10898,8 @@ snapshots: '@ampproject/remapping@2.2.1': dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 '@ark/attest@0.12.1(typescript@5.4.2)': dependencies: @@ -11368,27 +11340,25 @@ snapshots: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - '@babel/compat-data@7.21.4': {} - '@babel/compat-data@7.25.2': {} '@babel/core@7.21.4': dependencies: '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-module-transforms': 7.21.2 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.0 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.21.4) '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.4 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.3 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -11423,15 +11393,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-compilation-targets@7.21.4(@babel/core@7.21.4)': - dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.23.0 - lru-cache: 5.1.1 - semver: 6.3.0 - '@babel/helper-compilation-targets@7.25.2': dependencies: '@babel/compat-data': 7.25.2 @@ -11502,10 +11463,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.18.6': - dependencies: - '@babel/types': 7.21.4 - '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.25.3 @@ -11513,19 +11470,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.21.2': - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.20.2 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-transforms@7.25.2(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 @@ -11562,10 +11506,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-simple-access@7.20.2': - dependencies: - '@babel/types': 7.21.4 - '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.25.3 @@ -11596,8 +11536,6 @@ snapshots: '@babel/helper-validator-identifier@7.24.7': {} - '@babel/helper-validator-option@7.21.0': {} - '@babel/helper-validator-option@7.24.8': {} '@babel/helper-wrap-function@7.25.0': @@ -11610,9 +11548,9 @@ snapshots: '@babel/helpers@7.21.0': dependencies: - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -11708,7 +11646,7 @@ snapshots: '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.4)': dependencies: @@ -11718,7 +11656,7 @@ snapshots: '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.21.4)': dependencies: @@ -11758,12 +11696,12 @@ snapshots: '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-jsx@7.18.6(@babel/core@7.21.4)': dependencies: @@ -11778,32 +11716,32 @@ snapshots: '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.21.4)': dependencies: @@ -11813,7 +11751,7 @@ snapshots: '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.21.4)': dependencies: @@ -12112,12 +12050,12 @@ snapshots: '@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4)': dependencies: @@ -18352,7 +18290,7 @@ snapshots: handlebars@4.7.7: dependencies: - minimist: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) + minimist: 1.2.8 neo-async: 2.6.2 source-map: 0.6.1 wordwrap: 1.0.0 @@ -19431,7 +19369,7 @@ snapshots: json5@1.0.2: dependencies: - minimist: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) + minimist: 1.2.8 json5@2.2.3: {} @@ -20051,7 +19989,7 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimist@1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm): {} + minimist@1.2.8: {} minipass-collect@1.0.2: dependencies: @@ -20100,7 +20038,7 @@ snapshots: mkdirp@0.5.6: dependencies: - minimist: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) + minimist: 1.2.8 mkdirp@1.0.4: {} @@ -20539,7 +20477,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -20746,7 +20684,7 @@ snapshots: detect-libc: 2.0.2 expand-template: 2.0.3 github-from-package: 0.0.0 - minimist: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) + minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 1.0.2 node-abi: 3.45.0 @@ -20927,7 +20865,7 @@ snapshots: dependencies: deep-extend: 0.6.0 ini: 1.3.8 - minimist: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) + minimist: 1.2.8 strip-json-comments: 2.0.1 react-devtools-core@5.3.1(bufferutil@4.0.8)(utf-8-validate@5.0.10): @@ -21456,7 +21394,7 @@ snapshots: shx@0.3.4: dependencies: - minimist: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) + minimist: 1.2.8 shelljs: 0.8.5 side-channel@1.0.4: @@ -22111,7 +22049,7 @@ snapshots: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 - minimist: 1.2.8(patch_hash=tkbpkgnnti52zmnvbq3uwanedm) + minimist: 1.2.8 strip-bom: 3.0.0 tslib@1.14.1: {} From 854645260c41eaa89cdadad30bf8e70d5d2fd109 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Tue, 10 Sep 2024 17:50:37 +0000 Subject: [PATCH 08/20] feat(cli): deploy custom world (#3131) --- .changeset/heavy-mice-appear.md | 24 + .github/workflows/examples.yml | 6 +- docs/pages/config/reference.mdx | 21 +- examples/custom-world/.gitignore | 12 + examples/custom-world/.prettierrc | 8 + examples/custom-world/.solhint.json | 12 + examples/custom-world/foundry.toml | 29 + examples/custom-world/mud.config.ts | 22 + examples/custom-world/package.json | 35 ++ examples/custom-world/pnpm-workspace.yaml | 1 + examples/custom-world/remappings.txt | 3 + examples/custom-world/script/PostDeploy.s.sol | 33 ++ examples/custom-world/src/CustomWorld.sol | 10 + examples/custom-world/src/codegen/index.sol | 6 + .../custom-world/src/codegen/tables/Tasks.sol | 522 ++++++++++++++++++ .../src/codegen/world/ITasksSystem.sol | 19 + .../custom-world/src/codegen/world/IWorld.sol | 16 + .../custom-world/src/systems/TasksSystem.sol | 24 + examples/custom-world/test/TasksTest.t.sol | 30 + examples/custom-world/tsconfig.json | 3 + examples/custom-world/worlds.json.d.ts | 2 + examples/faucet-client/pnpm-workspace.yaml | 1 + examples/indexer-client/pnpm-workspace.yaml | 1 + .../minimal/packages/contracts/package.json | 4 +- examples/minimal/pnpm-lock.yaml | 20 +- examples/multiple-namespaces/package.json | 4 +- examples/multiple-namespaces/pnpm-lock.yaml | 20 +- .../multiple-namespaces/pnpm-workspace.yaml | 1 + packages/cli/src/deploy/deploy.ts | 24 +- packages/cli/src/deploy/deployCustomWorld.ts | 122 ++++ packages/cli/src/deploy/ensureContract.ts | 22 +- packages/cli/src/runDeploy.ts | 6 +- packages/world/ts/config/v2/defaults.ts | 1 - packages/world/ts/config/v2/input.ts | 19 +- packages/world/ts/config/v2/output.ts | 14 +- packages/world/ts/config/v2/world.test.ts | 4 - packages/world/ts/node/index.ts | 2 + .../phaser/packages/contracts/package.json | 4 +- .../react-ecs/packages/contracts/package.json | 4 +- .../react/packages/contracts/package.json | 4 +- .../threejs/packages/contracts/package.json | 4 +- .../vanilla/packages/contracts/package.json | 4 +- 42 files changed, 1047 insertions(+), 76 deletions(-) create mode 100644 .changeset/heavy-mice-appear.md create mode 100644 examples/custom-world/.gitignore create mode 100644 examples/custom-world/.prettierrc create mode 100644 examples/custom-world/.solhint.json create mode 100644 examples/custom-world/foundry.toml create mode 100644 examples/custom-world/mud.config.ts create mode 100644 examples/custom-world/package.json create mode 100644 examples/custom-world/pnpm-workspace.yaml create mode 100644 examples/custom-world/remappings.txt create mode 100644 examples/custom-world/script/PostDeploy.s.sol create mode 100644 examples/custom-world/src/CustomWorld.sol create mode 100644 examples/custom-world/src/codegen/index.sol create mode 100644 examples/custom-world/src/codegen/tables/Tasks.sol create mode 100644 examples/custom-world/src/codegen/world/ITasksSystem.sol create mode 100644 examples/custom-world/src/codegen/world/IWorld.sol create mode 100644 examples/custom-world/src/systems/TasksSystem.sol create mode 100644 examples/custom-world/test/TasksTest.t.sol create mode 100644 examples/custom-world/tsconfig.json create mode 100644 examples/custom-world/worlds.json.d.ts create mode 100644 packages/cli/src/deploy/deployCustomWorld.ts diff --git a/.changeset/heavy-mice-appear.md b/.changeset/heavy-mice-appear.md new file mode 100644 index 0000000000..532c835bcc --- /dev/null +++ b/.changeset/heavy-mice-appear.md @@ -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", + }, +}); +``` diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 0835ea7059..9c14c296c0 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -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 diff --git a/docs/pages/config/reference.mdx b/docs/pages/config/reference.mdx index e861e6e96f..a656854d70 100644 --- a/docs/pages/config/reference.mdx +++ b/docs/pages/config/reference.mdx @@ -128,18 +128,18 @@ The following options are available in both single- and multiple-namespace modes 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). - Whether or not to install this as a root module. Defaults to `false`. - A list of arguments used to call the module's install function. Each argument is a structure with two fields: - + + Solidity data type. 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). + @@ -154,6 +154,7 @@ The following options are available in both single- and multiple-namespace modes Filename relative to `outputDirectory` to codegen enums into. Defaults to `"common.sol"`. Output directory of world interfaces relative to `outputDirectory`. Defaults to `"world"`. + Customize how to deploy the world. @@ -162,7 +163,19 @@ The following options are available in both single- and multiple-namespace modes Directory, relative to project root, to write the deployment artifacts to. Defaults to `"deploys"`. Script name to execute after the deployment is complete. Defaults to `"PostDeploy"`. JSON filename, relative to project root, to write per-chain world deployment addresses. Defaults to `"worlds.json"`. - 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). + 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). + + 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. + + + Path to custom world source file relative to project root dir. + Contract name in custom world source file. + + + diff --git a/examples/custom-world/.gitignore b/examples/custom-world/.gitignore new file mode 100644 index 0000000000..a92132ba4f --- /dev/null +++ b/examples/custom-world/.gitignore @@ -0,0 +1,12 @@ +out/ +cache/ +node_modules/ +bindings/ +artifacts/ +broadcast/ + +pnpm-lock.yaml + +# Ignore MUD deploy artifacts +deploys/**/*.json +worlds.json diff --git a/examples/custom-world/.prettierrc b/examples/custom-world/.prettierrc new file mode 100644 index 0000000000..bf689e8c48 --- /dev/null +++ b/examples/custom-world/.prettierrc @@ -0,0 +1,8 @@ +{ + "plugins": ["prettier-plugin-solidity"], + "printWidth": 120, + "semi": true, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true +} diff --git a/examples/custom-world/.solhint.json b/examples/custom-world/.solhint.json new file mode 100644 index 0000000000..f3e0b01ffc --- /dev/null +++ b/examples/custom-world/.solhint.json @@ -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" + } +} diff --git a/examples/custom-world/foundry.toml b/examples/custom-world/foundry.toml new file mode 100644 index 0000000000..3256b0c658 --- /dev/null +++ b/examples/custom-world/foundry.toml @@ -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" diff --git a/examples/custom-world/mud.config.ts b/examples/custom-world/mud.config.ts new file mode 100644 index 0000000000..856f3a12b8 --- /dev/null +++ b/examples/custom-world/mud.config.ts @@ -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", + }, + }, +}); diff --git a/examples/custom-world/package.json b/examples/custom-world/package.json new file mode 100644 index 0000000000..9ec58ace1b --- /dev/null +++ b/examples/custom-world/package.json @@ -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" + } +} diff --git a/examples/custom-world/pnpm-workspace.yaml b/examples/custom-world/pnpm-workspace.yaml new file mode 100644 index 0000000000..8a14b6e24f --- /dev/null +++ b/examples/custom-world/pnpm-workspace.yaml @@ -0,0 +1 @@ +# this file forces pnpm to treat this separately from the monorepo root workspace diff --git a/examples/custom-world/remappings.txt b/examples/custom-world/remappings.txt new file mode 100644 index 0000000000..c4d992480e --- /dev/null +++ b/examples/custom-world/remappings.txt @@ -0,0 +1,3 @@ +ds-test/=node_modules/ds-test/src/ +forge-std/=node_modules/forge-std/src/ +@latticexyz/=node_modules/@latticexyz/ diff --git a/examples/custom-world/script/PostDeploy.s.sol b/examples/custom-world/script/PostDeploy.s.sol new file mode 100644 index 0000000000..93da4ddd8c --- /dev/null +++ b/examples/custom-world/script/PostDeploy.s.sol @@ -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(); + } +} diff --git a/examples/custom-world/src/CustomWorld.sol b/examples/custom-world/src/CustomWorld.sol new file mode 100644 index 0000000000..3141c69963 --- /dev/null +++ b/examples/custom-world/src/CustomWorld.sol @@ -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!"; + } +} diff --git a/examples/custom-world/src/codegen/index.sol b/examples/custom-world/src/codegen/index.sol new file mode 100644 index 0000000000..6337bbbc62 --- /dev/null +++ b/examples/custom-world/src/codegen/index.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +import { Tasks, TasksData } from "./tables/Tasks.sol"; diff --git a/examples/custom-world/src/codegen/tables/Tasks.sol b/examples/custom-world/src/codegen/tables/Tasks.sol new file mode 100644 index 0000000000..48535061b8 --- /dev/null +++ b/examples/custom-world/src/codegen/tables/Tasks.sol @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +struct TasksData { + uint256 createdAt; + uint256 completedAt; + string description; +} + +library Tasks { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "app", name: "Tasks", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x746261707000000000000000000000005461736b730000000000000000000000); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0040020120200000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (bytes32) + Schema constant _keySchema = Schema.wrap(0x002001005f000000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (uint256, uint256, string) + Schema constant _valueSchema = Schema.wrap(0x004002011f1fc500000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](1); + keyNames[0] = "id"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](3); + fieldNames[0] = "createdAt"; + fieldNames[1] = "completedAt"; + fieldNames[2] = "description"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get createdAt. + */ + function getCreatedAt(bytes32 id) internal view returns (uint256 createdAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get createdAt. + */ + function _getCreatedAt(bytes32 id) internal view returns (uint256 createdAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Set createdAt. + */ + function setCreatedAt(bytes32 id, uint256 createdAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((createdAt)), _fieldLayout); + } + + /** + * @notice Set createdAt. + */ + function _setCreatedAt(bytes32 id, uint256 createdAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((createdAt)), _fieldLayout); + } + + /** + * @notice Get completedAt. + */ + function getCompletedAt(bytes32 id) internal view returns (uint256 completedAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get completedAt. + */ + function _getCompletedAt(bytes32 id) internal view returns (uint256 completedAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Set completedAt. + */ + function setCompletedAt(bytes32 id, uint256 completedAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((completedAt)), _fieldLayout); + } + + /** + * @notice Set completedAt. + */ + function _setCompletedAt(bytes32 id, uint256 completedAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((completedAt)), _fieldLayout); + } + + /** + * @notice Get description. + */ + function getDescription(bytes32 id) internal view returns (string memory description) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get description. + */ + function _getDescription(bytes32 id) internal view returns (string memory description) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Set description. + */ + function setDescription(bytes32 id, string memory description) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, bytes((description))); + } + + /** + * @notice Set description. + */ + function _setDescription(bytes32 id, string memory description) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, bytes((description))); + } + + /** + * @notice Get the length of description. + */ + function lengthDescription(bytes32 id) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of description. + */ + function _lengthDescription(bytes32 id) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get an item of description. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemDescription(bytes32 id, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of description. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemDescription(bytes32 id, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Push a slice to description. + */ + function pushDescription(bytes32 id, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to description. + */ + function _pushDescription(bytes32 id, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Pop a slice from description. + */ + function popDescription(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from description. + */ + function _popDescription(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Update a slice of description at `_index`. + */ + function updateDescription(bytes32 id, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of description at `_index`. + */ + function _updateDescription(bytes32 id, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Get the full data. + */ + function get(bytes32 id) internal view returns (TasksData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + (bytes memory _staticData, EncodedLengths _encodedLengths, bytes memory _dynamicData) = StoreSwitch.getRecord( + _tableId, + _keyTuple, + _fieldLayout + ); + return decode(_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Get the full data. + */ + function _get(bytes32 id) internal view returns (TasksData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + (bytes memory _staticData, EncodedLengths _encodedLengths, bytes memory _dynamicData) = StoreCore.getRecord( + _tableId, + _keyTuple, + _fieldLayout + ); + return decode(_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using individual values. + */ + function set(bytes32 id, uint256 createdAt, uint256 completedAt, string memory description) internal { + bytes memory _staticData = encodeStatic(createdAt, completedAt); + + EncodedLengths _encodedLengths = encodeLengths(description); + bytes memory _dynamicData = encodeDynamic(description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using individual values. + */ + function _set(bytes32 id, uint256 createdAt, uint256 completedAt, string memory description) internal { + bytes memory _staticData = encodeStatic(createdAt, completedAt); + + EncodedLengths _encodedLengths = encodeLengths(description); + bytes memory _dynamicData = encodeDynamic(description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** + * @notice Set the full data using the data struct. + */ + function set(bytes32 id, TasksData memory _table) internal { + bytes memory _staticData = encodeStatic(_table.createdAt, _table.completedAt); + + EncodedLengths _encodedLengths = encodeLengths(_table.description); + bytes memory _dynamicData = encodeDynamic(_table.description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using the data struct. + */ + function _set(bytes32 id, TasksData memory _table) internal { + bytes memory _staticData = encodeStatic(_table.createdAt, _table.completedAt); + + EncodedLengths _encodedLengths = encodeLengths(_table.description); + bytes memory _dynamicData = encodeDynamic(_table.description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** + * @notice Decode the tightly packed blob of static data using this table's field layout. + */ + function decodeStatic(bytes memory _blob) internal pure returns (uint256 createdAt, uint256 completedAt) { + createdAt = (uint256(Bytes.getBytes32(_blob, 0))); + + completedAt = (uint256(Bytes.getBytes32(_blob, 32))); + } + + /** + * @notice Decode the tightly packed blob of dynamic data using the encoded lengths. + */ + function decodeDynamic( + EncodedLengths _encodedLengths, + bytes memory _blob + ) internal pure returns (string memory description) { + uint256 _start; + uint256 _end; + unchecked { + _end = _encodedLengths.atIndex(0); + } + description = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); + } + + /** + * @notice Decode the tightly packed blobs using this table's field layout. + * @param _staticData Tightly packed static fields. + * @param _encodedLengths Encoded lengths of dynamic fields. + * @param _dynamicData Tightly packed dynamic fields. + */ + function decode( + bytes memory _staticData, + EncodedLengths _encodedLengths, + bytes memory _dynamicData + ) internal pure returns (TasksData memory _table) { + (_table.createdAt, _table.completedAt) = decodeStatic(_staticData); + + (_table.description) = decodeDynamic(_encodedLengths, _dynamicData); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(uint256 createdAt, uint256 completedAt) internal pure returns (bytes memory) { + return abi.encodePacked(createdAt, completedAt); + } + + /** + * @notice Tightly pack dynamic data lengths using this table's schema. + * @return _encodedLengths The lengths of the dynamic fields (packed into a single bytes32 value). + */ + function encodeLengths(string memory description) internal pure returns (EncodedLengths _encodedLengths) { + // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits + unchecked { + _encodedLengths = EncodedLengthsLib.pack(bytes(description).length); + } + } + + /** + * @notice Tightly pack dynamic (variable length) data using this table's schema. + * @return The dynamic data, encoded into a sequence of bytes. + */ + function encodeDynamic(string memory description) internal pure returns (bytes memory) { + return abi.encodePacked(bytes((description))); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode( + uint256 createdAt, + uint256 completedAt, + string memory description + ) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(createdAt, completedAt); + + EncodedLengths _encodedLengths = encodeLengths(description); + bytes memory _dynamicData = encodeDynamic(description); + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(bytes32 id) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + return _keyTuple; + } +} diff --git a/examples/custom-world/src/codegen/world/ITasksSystem.sol b/examples/custom-world/src/codegen/world/ITasksSystem.sol new file mode 100644 index 0000000000..7ac8215848 --- /dev/null +++ b/examples/custom-world/src/codegen/world/ITasksSystem.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +/** + * @title ITasksSystem + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + */ +interface ITasksSystem { + function app__addTask(string memory description) external returns (bytes32 id); + + function app__completeTask(bytes32 id) external; + + function app__resetTask(bytes32 id) external; + + function app__deleteTask(bytes32 id) external; +} diff --git a/examples/custom-world/src/codegen/world/IWorld.sol b/examples/custom-world/src/codegen/world/IWorld.sol new file mode 100644 index 0000000000..71352c40ca --- /dev/null +++ b/examples/custom-world/src/codegen/world/IWorld.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; +import { ITasksSystem } from "./ITasksSystem.sol"; + +/** + * @title IWorld + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @notice This interface integrates all systems and associated function selectors + * that are dynamically registered in the World during deployment. + * @dev This is an autogenerated file; do not edit manually. + */ +interface IWorld is IBaseWorld, ITasksSystem {} diff --git a/examples/custom-world/src/systems/TasksSystem.sol b/examples/custom-world/src/systems/TasksSystem.sol new file mode 100644 index 0000000000..e9a5654f2c --- /dev/null +++ b/examples/custom-world/src/systems/TasksSystem.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { System } from "@latticexyz/world/src/System.sol"; +import { Tasks, TasksData } from "../codegen/index.sol"; + +contract TasksSystem is System { + function addTask(string memory description) public returns (bytes32 id) { + id = keccak256(abi.encode(block.prevrandao, _msgSender(), description)); + Tasks.set(id, TasksData({ description: description, createdAt: block.timestamp, completedAt: 0 })); + } + + function completeTask(bytes32 id) public { + Tasks.setCompletedAt(id, block.timestamp); + } + + function resetTask(bytes32 id) public { + Tasks.setCompletedAt(id, 0); + } + + function deleteTask(bytes32 id) public { + Tasks.deleteRecord(id); + } +} diff --git a/examples/custom-world/test/TasksTest.t.sol b/examples/custom-world/test/TasksTest.t.sol new file mode 100644 index 0000000000..69afe731f2 --- /dev/null +++ b/examples/custom-world/test/TasksTest.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import "forge-std/Test.sol"; +import { MudTest } from "@latticexyz/world/test/MudTest.t.sol"; + +import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { Tasks, TasksData } from "../src/codegen/index.sol"; + +contract TasksTest is MudTest { + function testWorldExists() public { + uint256 codeSize; + address addr = worldAddress; + assembly { + codeSize := extcodesize(addr) + } + assertTrue(codeSize > 0); + } + + function testTasks() public { + // Expect task to exist that we created during PostDeploy script + TasksData memory task = Tasks.get("1"); + assertEq(task.description, "Walk the dog"); + assertEq(task.completedAt, 0); + + // Expect the task to be completed after calling completeTask from our TasksSystem + IWorld(worldAddress).app__completeTask("1"); + assertEq(Tasks.getCompletedAt("1"), block.timestamp); + } +} diff --git a/examples/custom-world/tsconfig.json b/examples/custom-world/tsconfig.json new file mode 100644 index 0000000000..4082f16a5d --- /dev/null +++ b/examples/custom-world/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/examples/custom-world/worlds.json.d.ts b/examples/custom-world/worlds.json.d.ts new file mode 100644 index 0000000000..494829c2f9 --- /dev/null +++ b/examples/custom-world/worlds.json.d.ts @@ -0,0 +1,2 @@ +declare const worlds: Partial>; +export default worlds; diff --git a/examples/faucet-client/pnpm-workspace.yaml b/examples/faucet-client/pnpm-workspace.yaml index e69de29bb2..8a14b6e24f 100644 --- a/examples/faucet-client/pnpm-workspace.yaml +++ b/examples/faucet-client/pnpm-workspace.yaml @@ -0,0 +1 @@ +# this file forces pnpm to treat this separately from the monorepo root workspace diff --git a/examples/indexer-client/pnpm-workspace.yaml b/examples/indexer-client/pnpm-workspace.yaml index e69de29bb2..8a14b6e24f 100644 --- a/examples/indexer-client/pnpm-workspace.yaml +++ b/examples/indexer-client/pnpm-workspace.yaml @@ -0,0 +1 @@ +# this file forces pnpm to treat this separately from the monorepo root workspace diff --git a/examples/minimal/packages/contracts/package.json b/examples/minimal/packages/contracts/package.json index 8e0740c3d3..616f82f625 100644 --- a/examples/minimal/packages/contracts/package.json +++ b/examples/minimal/packages/contracts/package.json @@ -28,8 +28,8 @@ "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "solhint": "^3.4.1", - "solhint-config-mud": "file:../../../../packages/solhint-config-mud", - "solhint-plugin-mud": "file:../../../../packages/solhint-plugin-mud", + "solhint-config-mud": "link:../../../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../../../packages/solhint-plugin-mud", "typescript": "5.4.2" } } diff --git a/examples/minimal/pnpm-lock.yaml b/examples/minimal/pnpm-lock.yaml index 0b06dc1316..2e4a085f95 100644 --- a/examples/minimal/pnpm-lock.yaml +++ b/examples/minimal/pnpm-lock.yaml @@ -339,11 +339,11 @@ importers: specifier: ^3.4.1 version: 3.4.1 solhint-config-mud: - specifier: file:../../../../packages/solhint-config-mud - version: file:../../packages/solhint-config-mud + specifier: link:../../../../packages/solhint-config-mud + version: link:../../../../packages/solhint-config-mud solhint-plugin-mud: - specifier: file:../../../../packages/solhint-plugin-mud - version: file:../../packages/solhint-plugin-mud + specifier: link:../../../../packages/solhint-plugin-mud + version: link:../../../../packages/solhint-plugin-mud typescript: specifier: 5.4.2 version: 5.4.2 @@ -1816,12 +1816,6 @@ packages: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} - solhint-config-mud@file:../../packages/solhint-config-mud: - resolution: {directory: ../../packages/solhint-config-mud, type: directory} - - solhint-plugin-mud@file:../../packages/solhint-plugin-mud: - resolution: {directory: ../../packages/solhint-plugin-mud, type: directory} - solhint@3.4.1: resolution: {integrity: sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg==} hasBin: true @@ -3610,12 +3604,6 @@ snapshots: astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 - solhint-config-mud@file:../../packages/solhint-config-mud: {} - - solhint-plugin-mud@file:../../packages/solhint-plugin-mud: - dependencies: - '@solidity-parser/parser': 0.16.0 - solhint@3.4.1: dependencies: '@solidity-parser/parser': 0.16.0 diff --git a/examples/multiple-namespaces/package.json b/examples/multiple-namespaces/package.json index ea256ecfee..f727002746 100644 --- a/examples/multiple-namespaces/package.json +++ b/examples/multiple-namespaces/package.json @@ -27,8 +27,8 @@ "prettier-plugin-solidity": "1.3.1", "shx": "^0.3.4", "solhint": "^3.4.1", - "solhint-config-mud": "file:../../packages/solhint-config-mud", - "solhint-plugin-mud": "file:../../packages/solhint-plugin-mud", + "solhint-config-mud": "link:../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../packages/solhint-plugin-mud", "typescript": "5.4.2" } } diff --git a/examples/multiple-namespaces/pnpm-lock.yaml b/examples/multiple-namespaces/pnpm-lock.yaml index a610e2b56b..a6a945bcc1 100644 --- a/examples/multiple-namespaces/pnpm-lock.yaml +++ b/examples/multiple-namespaces/pnpm-lock.yaml @@ -51,11 +51,11 @@ importers: specifier: ^3.4.1 version: 3.6.2(typescript@5.4.2) solhint-config-mud: - specifier: file:../../packages/solhint-config-mud - version: file:../../packages/solhint-config-mud + specifier: link:../../packages/solhint-config-mud + version: link:../../packages/solhint-config-mud solhint-plugin-mud: - specifier: file:../../packages/solhint-plugin-mud - version: file:../../packages/solhint-plugin-mud + specifier: link:../../packages/solhint-plugin-mud + version: link:../../packages/solhint-plugin-mud typescript: specifier: 5.4.2 version: 5.4.2 @@ -374,12 +374,6 @@ packages: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} - solhint-config-mud@file:../../packages/solhint-config-mud: - resolution: {directory: ../../packages/solhint-config-mud, type: directory} - - solhint-plugin-mud@file:../../packages/solhint-plugin-mud: - resolution: {directory: ../../packages/solhint-plugin-mud, type: directory} - solhint@3.6.2: resolution: {integrity: sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ==} hasBin: true @@ -710,12 +704,6 @@ snapshots: astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 - solhint-config-mud@file:../../packages/solhint-config-mud: {} - - solhint-plugin-mud@file:../../packages/solhint-plugin-mud: - dependencies: - '@solidity-parser/parser': 0.16.2 - solhint@3.6.2(typescript@5.4.2): dependencies: '@solidity-parser/parser': 0.16.2 diff --git a/examples/multiple-namespaces/pnpm-workspace.yaml b/examples/multiple-namespaces/pnpm-workspace.yaml index e69de29bb2..8a14b6e24f 100644 --- a/examples/multiple-namespaces/pnpm-workspace.yaml +++ b/examples/multiple-namespaces/pnpm-workspace.yaml @@ -0,0 +1 @@ +# this file forces pnpm to treat this separately from the monorepo root workspace diff --git a/packages/cli/src/deploy/deploy.ts b/packages/cli/src/deploy/deploy.ts index 70efef50c5..610b8634ce 100644 --- a/packages/cli/src/deploy/deploy.ts +++ b/packages/cli/src/deploy/deploy.ts @@ -16,13 +16,18 @@ import { ensureWorldFactory } from "./ensureWorldFactory"; import { Table } from "@latticexyz/config"; import { ensureResourceTags } from "./ensureResourceTags"; import { waitForTransactions } from "./waitForTransactions"; +import { ContractArtifact } from "@latticexyz/world/node"; +import { World } from "@latticexyz/world"; +import { deployCustomWorld } from "./deployCustomWorld"; type DeployOptions = { + config: World; client: Client; tables: readonly Table[]; systems: readonly System[]; libraries: readonly Library[]; modules?: readonly Module[]; + artifacts: readonly ContractArtifact[]; salt?: Hex; worldAddress?: Address; /** @@ -42,19 +47,20 @@ type DeployOptions = { * replace systems, etc.) */ export async function deploy({ + config, client, tables, systems, libraries, modules = [], + artifacts, salt, worldAddress: existingWorldAddress, deployerAddress: initialDeployerAddress, - withWorldProxy, }: DeployOptions): Promise { const deployerAddress = initialDeployerAddress ?? (await ensureDeployer(client)); - await ensureWorldFactory(client, deployerAddress, withWorldProxy); + await ensureWorldFactory(client, deployerAddress, config.deploy.upgradeableWorldImplementation); // deploy all dependent contracts, because system registration, module install, etc. all expect these contracts to be callable. await ensureContractsDeployed({ @@ -81,7 +87,19 @@ export async function deploy({ const worldDeploy = existingWorldAddress ? await getWorldDeploy(client, existingWorldAddress) - : await deployWorld(client, deployerAddress, salt ?? `0x${randomBytes(32).toString("hex")}`, withWorldProxy); + : config.deploy.customWorld + ? await deployCustomWorld({ + client, + deployerAddress, + artifacts, + customWorld: config.deploy.customWorld, + }) + : await deployWorld( + client, + deployerAddress, + salt ?? `0x${randomBytes(32).toString("hex")}`, + config.deploy.upgradeableWorldImplementation, + ); if (!supportedStoreVersions.includes(worldDeploy.storeVersion)) { throw new Error(`Unsupported Store version: ${worldDeploy.storeVersion}`); diff --git a/packages/cli/src/deploy/deployCustomWorld.ts b/packages/cli/src/deploy/deployCustomWorld.ts new file mode 100644 index 0000000000..8be55baaa7 --- /dev/null +++ b/packages/cli/src/deploy/deployCustomWorld.ts @@ -0,0 +1,122 @@ +import { Account, Chain, Client, Hex, Transport, concatHex, encodeDeployData, getCreate2Address, isHex } from "viem"; +import { waitForTransactionReceipt } from "viem/actions"; +import { resourceToHex, sendTransaction, writeContract } from "@latticexyz/common"; +import { debug } from "./debug"; +import { logsToWorldDeploy } from "./logsToWorldDeploy"; +import { WorldDeploy, salt, worldAbi } from "./common"; +import { getWorldContracts } from "./getWorldContracts"; +import { ensureContractsDeployed } from "./ensureContractsDeployed"; +import { ContractArtifact, ReferenceIdentifier } from "@latticexyz/world/node"; +import { World } from "@latticexyz/world"; +import { waitForTransactions } from "./waitForTransactions"; + +function findArtifact(ref: ReferenceIdentifier, artifacts: readonly ContractArtifact[]): ContractArtifact { + const artifact = artifacts.find((a) => a.sourcePath === ref.sourcePath && a.name === ref.name); + if (!artifact) throw new Error(`Could not find referenced artifact at "${ref.sourcePath}:${ref.name}".`); + return artifact; +} + +function getDependencies( + artifact: ContractArtifact, + artifacts: readonly ContractArtifact[], +): readonly ContractArtifact[] { + return artifact.bytecode + .filter((part): part is Exclude => !isHex(part)) + .flatMap((ref) => { + return getDependencies(findArtifact(ref, artifacts), artifacts); + }); +} + +function getDeployable(deployerAddress: Hex, artifact: ContractArtifact, artifacts: readonly ContractArtifact[]): Hex { + return concatHex( + artifact.bytecode.map((ref): Hex => { + if (isHex(ref)) return ref; + return getCreate2Address({ + from: deployerAddress, + salt, + bytecode: getDeployable(deployerAddress, findArtifact(ref, artifacts), artifacts), + }); + }), + ); +} + +export async function deployCustomWorld({ + client, + deployerAddress, + artifacts, + customWorld, +}: { + client: Client; + deployerAddress: Hex; + artifacts: readonly ContractArtifact[]; + customWorld: Exclude; +}): Promise { + // deploy world prereqs (e.g. core modules) + const contracts = getWorldContracts(deployerAddress); + await ensureContractsDeployed({ + client, + deployerAddress, + contracts: Object.values(contracts), + }); + + const worldArtifact = findArtifact(customWorld, artifacts); + // TODO: check that world ABI still satisfies the protocol (https://github.com/latticexyz/mud/issues/3152) + + // Find and deploy dependencies (i.e. public libraries) + const deps = getDependencies(worldArtifact, artifacts); + if (deps.length) { + debug(`deploying ${deps.length} world dependencies`); + await ensureContractsDeployed({ + client, + deployerAddress, + contracts: deps + .map((dep) => getDeployable(deployerAddress, dep, artifacts)) + .reverse() + .map((bytecode) => ({ bytecode })), + }); + } + + // Deploy custom world without deterministic deployer for now + debug("deploying custom world"); + const deployTx = await sendTransaction(client, { + chain: client.chain ?? null, + data: encodeDeployData({ + abi: worldArtifact.abi, + args: [], // TODO (https://github.com/latticexyz/mud/issues/3150) + bytecode: getDeployable(deployerAddress, worldArtifact, artifacts), + }), + }); + + debug("waiting for custom world deploy"); + const receipt = await waitForTransactionReceipt(client, { hash: deployTx }); + if (receipt.status !== "success") { + console.error("world deploy failed", receipt); + throw new Error("world deploy failed"); + } + + const deploy = logsToWorldDeploy(receipt.logs); + debug("deployed custom world to", deploy.address, "at block", deploy.deployBlock); + + const initTxs = await Promise.all([ + // initialize world via init module + writeContract(client, { + chain: client.chain ?? null, + address: deploy.address, + abi: worldAbi, + functionName: "initialize", + args: [contracts.InitModule.address], + }), + // transfer root namespace to deployer + writeContract(client, { + chain: client.chain ?? null, + address: deploy.address, + abi: worldAbi, + functionName: "transferOwnership", + args: [resourceToHex({ type: "namespace", namespace: "", name: "" }), client.account.address], + }), + ]); + + await waitForTransactions({ client, hashes: initTxs, debugLabel: "world init" }); + + return { ...deploy, stateBlock: deploy.deployBlock }; +} diff --git a/packages/cli/src/deploy/ensureContract.ts b/packages/cli/src/deploy/ensureContract.ts index 5a4cfd4925..c02f93965a 100644 --- a/packages/cli/src/deploy/ensureContract.ts +++ b/packages/cli/src/deploy/ensureContract.ts @@ -7,7 +7,7 @@ import pRetry from "p-retry"; export type Contract = { bytecode: Hex; - deployedBytecodeSize: number; + deployedBytecodeSize?: number; debugLabel?: string; }; @@ -33,15 +33,17 @@ export async function ensureContract({ return []; } - if (deployedBytecodeSize > contractSizeLimit) { - console.warn( - `\nBytecode for ${debugLabel} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`, - ); - } else if (deployedBytecodeSize > contractSizeLimit * 0.95) { - console.warn( - // eslint-disable-next-line max-len - `\nBytecode for ${debugLabel} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`, - ); + if (deployedBytecodeSize != null) { + if (deployedBytecodeSize > contractSizeLimit) { + console.warn( + `\nBytecode for ${debugLabel} (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`, + ); + } else if (deployedBytecodeSize > contractSizeLimit * 0.95) { + console.warn( + // eslint-disable-next-line max-len + `\nBytecode for ${debugLabel} (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`, + ); + } } debug("deploying", debugLabel, "at", address); diff --git a/packages/cli/src/runDeploy.ts b/packages/cli/src/runDeploy.ts index 09566279bd..fb3fb3ebba 100644 --- a/packages/cli/src/runDeploy.ts +++ b/packages/cli/src/runDeploy.ts @@ -16,6 +16,7 @@ import { WorldDeploy } from "./deploy/common"; import { build } from "./build"; import { kmsKeyToAccount } from "@latticexyz/common/kms"; import { configToModules } from "./deploy/configToModules"; +import { findContractArtifacts } from "@latticexyz/world/node"; import { enableAutomine } from "./utils/enableAutomine"; export const deployOptions = { @@ -90,6 +91,8 @@ export async function runDeploy(opts: DeployOptions): Promise { config, forgeOutDir: outDir, }); + const artifacts = await findContractArtifacts({ forgeOutDir: outDir }); + // TODO: pass artifacts into configToModules (https://github.com/latticexyz/mud/issues/3153) const modules = await configToModules(config, outDir); const tables = Object.values(config.namespaces) @@ -139,6 +142,7 @@ export async function runDeploy(opts: DeployOptions): Promise { const startTime = Date.now(); const worldDeploy = await deploy({ + config, deployerAddress: opts.deployerAddress as Hex | undefined, salt, worldAddress: opts.worldAddress as Hex | undefined, @@ -147,7 +151,7 @@ export async function runDeploy(opts: DeployOptions): Promise { systems, libraries, modules, - withWorldProxy: config.deploy.upgradeableWorldImplementation, + artifacts, }); if (opts.worldAddress == null || opts.alwaysRunPostDeploy) { await postDeploy( diff --git a/packages/world/ts/config/v2/defaults.ts b/packages/world/ts/config/v2/defaults.ts index 8a965d6d80..9cff871f29 100644 --- a/packages/world/ts/config/v2/defaults.ts +++ b/packages/world/ts/config/v2/defaults.ts @@ -32,7 +32,6 @@ export const CODEGEN_DEFAULTS = { export type CODEGEN_DEFAULTS = typeof CODEGEN_DEFAULTS; export const DEPLOY_DEFAULTS = { - customWorldContract: undefined, postDeployScript: "PostDeploy", deploysDirectory: "./deploys", worldsFile: "./worlds.json", diff --git a/packages/world/ts/config/v2/input.ts b/packages/world/ts/config/v2/input.ts index e18f10458c..97b056a994 100644 --- a/packages/world/ts/config/v2/input.ts +++ b/packages/world/ts/config/v2/input.ts @@ -69,7 +69,7 @@ export type ModuleInput = ModuleInputArtifactPath & { /** * Should this module be installed as a root module? * @default false - * */ + */ readonly root?: boolean; /** Arguments to be passed to the module's install method */ // TODO: make more strongly typed by taking in tables input @@ -77,9 +77,6 @@ export type ModuleInput = ModuleInputArtifactPath & { }; export type DeployInput = { - /** The name of a custom World contract to deploy. If no name is provided, a default MUD World is deployed. */ - // TODO: implement - readonly customWorldContract?: never; /** * Script to execute after the deployment is complete (Default "PostDeploy"). * Script must be placed in the forge scripts directory (see foundry.toml) and have a ".s.sol" extension. @@ -91,6 +88,20 @@ export type DeployInput = { readonly worldsFile?: string; /** Deploy the World as an upgradeable proxy */ readonly upgradeableWorldImplementation?: boolean; + /** + * 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](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. + */ + // TODO: enforce that this can't be used with `upgradeableWorldImplementation` (https://github.com/latticexyz/mud/issues/3151) + readonly customWorld?: { + /** Path to custom world source file relative to project root dir. */ + sourcePath: string; + /** Contract name in custom world source file. */ + name: string; + // TODO: constructor calldata (https://github.com/latticexyz/mud/issues/3150) + }; }; export type CodegenInput = Partial; diff --git a/packages/world/ts/config/v2/output.ts b/packages/world/ts/config/v2/output.ts index 1bf8cb6427..77e5ec2af1 100644 --- a/packages/world/ts/config/v2/output.ts +++ b/packages/world/ts/config/v2/output.ts @@ -80,8 +80,6 @@ export type Namespaces = { }; export type Deploy = { - /** The name of a custom World contract to deploy. If no name is provided, a default MUD World is deployed */ - readonly customWorldContract: string | undefined; /** * Script to execute after the deployment is complete (Default "PostDeploy"). * Script must be placed in the forge scripts directory (see foundry.toml) and have a ".s.sol" extension. @@ -93,6 +91,18 @@ export type Deploy = { readonly worldsFile: string; /** Deploy the World as an upgradeable proxy */ readonly upgradeableWorldImplementation: boolean; + /** + * 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](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. + */ + readonly customWorld?: { + /** Path to custom world source file relative to project root dir. */ + sourcePath: string; + /** Contract name in custom world source file. */ + name: string; + }; }; export type Codegen = { diff --git a/packages/world/ts/config/v2/world.test.ts b/packages/world/ts/config/v2/world.test.ts index e055253980..09f546966a 100644 --- a/packages/world/ts/config/v2/world.test.ts +++ b/packages/world/ts/config/v2/world.test.ts @@ -194,7 +194,6 @@ describe("defineWorld", () => { systems: {}, excludeSystems: [], deploy: { - customWorldContract: "(undefined)", postDeployScript: "PostDeploy", deploysDirectory: "./deploys", worldsFile: "./worlds.json", @@ -293,7 +292,6 @@ describe("defineWorld", () => { readonly excludeSystems: readonly [] readonly modules: readonly [] readonly deploy: { - readonly customWorldContract: undefined readonly postDeployScript: "PostDeploy" readonly deploysDirectory: "./deploys" readonly worldsFile: "./worlds.json" @@ -379,7 +377,6 @@ describe("defineWorld", () => { systems: {}, excludeSystems: [], deploy: { - customWorldContract: "(undefined)", postDeployScript: "PostDeploy", deploysDirectory: "./deploys", worldsFile: "./worlds.json", @@ -478,7 +475,6 @@ describe("defineWorld", () => { readonly excludeSystems: readonly [] readonly modules: readonly [] readonly deploy: { - readonly customWorldContract: undefined readonly postDeployScript: "PostDeploy" readonly deploysDirectory: "./deploys" readonly worldsFile: "./worlds.json" diff --git a/packages/world/ts/node/index.ts b/packages/world/ts/node/index.ts index bbfa3a8013..f723921116 100644 --- a/packages/world/ts/node/index.ts +++ b/packages/world/ts/node/index.ts @@ -1,6 +1,8 @@ export * from "./render-solidity"; +export * from "./findContractArtifacts"; export * from "./findSolidityFiles"; export * from "./getSystemContracts"; export * from "./resolveSystems"; export * from "./buildSystemsManifest"; export * from "./loadSystemsManifest"; +export * from "./common"; diff --git a/templates/phaser/packages/contracts/package.json b/templates/phaser/packages/contracts/package.json index 411cdb4969..b0a118d096 100644 --- a/templates/phaser/packages/contracts/package.json +++ b/templates/phaser/packages/contracts/package.json @@ -29,7 +29,7 @@ "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "solhint": "^3.3.7", - "solhint-config-mud": "file:../../../../packages/solhint-config-mud", - "solhint-plugin-mud": "file:../../../../packages/solhint-plugin-mud" + "solhint-config-mud": "link:../../../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../../../packages/solhint-plugin-mud" } } diff --git a/templates/react-ecs/packages/contracts/package.json b/templates/react-ecs/packages/contracts/package.json index 411cdb4969..b0a118d096 100644 --- a/templates/react-ecs/packages/contracts/package.json +++ b/templates/react-ecs/packages/contracts/package.json @@ -29,7 +29,7 @@ "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "solhint": "^3.3.7", - "solhint-config-mud": "file:../../../../packages/solhint-config-mud", - "solhint-plugin-mud": "file:../../../../packages/solhint-plugin-mud" + "solhint-config-mud": "link:../../../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../../../packages/solhint-plugin-mud" } } diff --git a/templates/react/packages/contracts/package.json b/templates/react/packages/contracts/package.json index 411cdb4969..b0a118d096 100644 --- a/templates/react/packages/contracts/package.json +++ b/templates/react/packages/contracts/package.json @@ -29,7 +29,7 @@ "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "solhint": "^3.3.7", - "solhint-config-mud": "file:../../../../packages/solhint-config-mud", - "solhint-plugin-mud": "file:../../../../packages/solhint-plugin-mud" + "solhint-config-mud": "link:../../../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../../../packages/solhint-plugin-mud" } } diff --git a/templates/threejs/packages/contracts/package.json b/templates/threejs/packages/contracts/package.json index 0c53d91d56..eb4384df7f 100644 --- a/templates/threejs/packages/contracts/package.json +++ b/templates/threejs/packages/contracts/package.json @@ -28,7 +28,7 @@ "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "solhint": "^3.3.7", - "solhint-config-mud": "file:../../../../packages/solhint-config-mud", - "solhint-plugin-mud": "file:../../../../packages/solhint-plugin-mud" + "solhint-config-mud": "link:../../../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../../../packages/solhint-plugin-mud" } } diff --git a/templates/vanilla/packages/contracts/package.json b/templates/vanilla/packages/contracts/package.json index 411cdb4969..b0a118d096 100644 --- a/templates/vanilla/packages/contracts/package.json +++ b/templates/vanilla/packages/contracts/package.json @@ -29,7 +29,7 @@ "prettier": "3.2.5", "prettier-plugin-solidity": "1.3.1", "solhint": "^3.3.7", - "solhint-config-mud": "file:../../../../packages/solhint-config-mud", - "solhint-plugin-mud": "file:../../../../packages/solhint-plugin-mud" + "solhint-config-mud": "link:../../../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../../../packages/solhint-plugin-mud" } } From 5277399692c805c8b2b80274888bad3a7e527048 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Tue, 10 Sep 2024 17:54:11 +0000 Subject: [PATCH 09/20] chore: remove query package (#3154) --- packages/query/CHANGELOG.md | 437 ------------------------- packages/query/README.md | 3 - packages/query/package.json | 51 --- packages/query/src/api.ts | 89 ----- packages/query/src/common.ts | 13 - packages/query/src/exports/index.ts | 22 -- packages/query/src/exports/internal.ts | 3 - packages/query/src/findSubjects.ts | 89 ----- packages/query/src/index.ts | 3 - packages/query/src/matchRecords.ts | 37 --- packages/query/tsconfig.json | 7 - packages/query/tsup.config.ts | 14 - packages/store-sync/package.json | 1 - pnpm-lock.yaml | 31 -- 14 files changed, 800 deletions(-) delete mode 100644 packages/query/CHANGELOG.md delete mode 100644 packages/query/README.md delete mode 100644 packages/query/package.json delete mode 100644 packages/query/src/api.ts delete mode 100644 packages/query/src/common.ts delete mode 100644 packages/query/src/exports/index.ts delete mode 100644 packages/query/src/exports/internal.ts delete mode 100644 packages/query/src/findSubjects.ts delete mode 100644 packages/query/src/index.ts delete mode 100644 packages/query/src/matchRecords.ts delete mode 100644 packages/query/tsconfig.json delete mode 100644 packages/query/tsup.config.ts diff --git a/packages/query/CHANGELOG.md b/packages/query/CHANGELOG.md deleted file mode 100644 index 6b70263cae..0000000000 --- a/packages/query/CHANGELOG.md +++ /dev/null @@ -1,437 +0,0 @@ -# @latticexyz/query - -## 2.2.2 - -### Patch Changes - -- @latticexyz/common@2.2.2 -- @latticexyz/config@2.2.2 -- @latticexyz/schema-type@2.2.2 -- @latticexyz/store@2.2.2 - -## 2.2.1 - -### Patch Changes - -- Updated dependencies [c0764a5] - - @latticexyz/common@2.2.1 - - @latticexyz/config@2.2.1 - - @latticexyz/store@2.2.1 - - @latticexyz/schema-type@2.2.1 - -## 2.2.0 - -### Patch Changes - -- Updated dependencies [69cd0a1] -- Updated dependencies [04c675c] - - @latticexyz/common@2.2.0 - - @latticexyz/config@2.2.0 - - @latticexyz/store@2.2.0 - - @latticexyz/schema-type@2.2.0 - -## 2.1.1 - -### Patch Changes - -- 9e21e42: Bumped viem to `2.19.8` and abitype to `1.0.5`. - - MUD projects using viem or abitype should do the same to ensure no type errors due to mismatched versions: - - ``` - pnpm recursive up viem@2.19.8 abitype@1.0.5 - ``` - -- Updated dependencies [9e21e42] -- Updated dependencies [2daaab1] -- Updated dependencies [57bf8c3] - - @latticexyz/common@2.1.1 - - @latticexyz/config@2.1.1 - - @latticexyz/schema-type@2.1.1 - - @latticexyz/store@2.1.1 - -## 2.1.0 - -### Patch Changes - -- 7129a16: Bumped `@arktype/util` and moved `evaluate`/`satisfy` usages to its `show`/`satisfy` helpers. -- Updated dependencies [24e285d] -- Updated dependencies [7129a16] -- Updated dependencies [7129a16] -- Updated dependencies [e85dc53] -- Updated dependencies [a10b453] -- Updated dependencies [69eb63b] -- Updated dependencies [8d0453e] -- Updated dependencies [fb1cfef] - - @latticexyz/store@2.1.0 - - @latticexyz/config@2.1.0 - - @latticexyz/common@2.1.0 - - @latticexyz/schema-type@2.1.0 - -## 2.0.12 - -### Patch Changes - -- 96e7bf430: TS source has been removed from published packages in favor of DTS in an effort to improve TS performance. All packages now inherit from a base TS config in `@latticexyz/common` to allow us to continue iterating on TS performance without requiring changes in your project code. - - If you have a MUD project that you're upgrading, we suggest adding a `tsconfig.json` file to your project workspace that extends this base config. - - ```sh - pnpm add -D @latticexyz/common - echo "{\n \"extends\": \"@latticexyz/common/tsconfig.base.json\"\n}" > tsconfig.json - ``` - - Then in each package of your project, inherit from your workspace root's config. - - For example, your TS config in `packages/contracts/tsconfig.json` might look like: - - ```json - { - "extends": "../../tsconfig.json" - } - ``` - - And your TS config in `packages/client/tsconfig.json` might look like: - - ```json - { - "extends": "../../tsconfig.json", - "compilerOptions": { - "types": ["vite/client"], - "target": "ESNext", - "lib": ["ESNext", "DOM"], - "jsx": "react-jsx", - "jsxImportSource": "react" - }, - "include": ["src"] - } - ``` - - You may need to adjust the above configs to include any additional TS options you've set. This config pattern may also reveal new TS errors that need to be fixed or rules disabled. - - If you want to keep your existing TS configs, we recommend at least updating your `moduleResolution` setting. - - ```diff - -"moduleResolution": "node" - +"moduleResolution": "Bundler" - ``` - -- Updated dependencies [c10c9fb2d] -- Updated dependencies [c10c9fb2d] -- Updated dependencies [96e7bf430] - - @latticexyz/store@2.0.12 - - @latticexyz/common@2.0.12 - - @latticexyz/config@2.0.12 - - @latticexyz/schema-type@2.0.12 - -## 2.0.11 - -### Patch Changes - -- @latticexyz/common@2.0.11 -- @latticexyz/config@2.0.11 -- @latticexyz/schema-type@2.0.11 -- @latticexyz/store@2.0.11 - -## 2.0.10 - -### Patch Changes - -- Updated dependencies [4e4e9104] -- Updated dependencies [51b137d3] -- Updated dependencies [32c1cda6] -- Updated dependencies [4caca05e] -- Updated dependencies [27f888c7] - - @latticexyz/store@2.0.10 - - @latticexyz/common@2.0.10 - - @latticexyz/config@2.0.10 - - @latticexyz/schema-type@2.0.10 - -## 2.0.9 - -### Patch Changes - -- Updated dependencies [764ca0a0] -- Updated dependencies [bad3ad1b] - - @latticexyz/common@2.0.9 - - @latticexyz/config@2.0.9 - - @latticexyz/store@2.0.9 - - @latticexyz/schema-type@2.0.9 - -## 2.0.8 - -### Patch Changes - -- Updated dependencies [df4781ac] - - @latticexyz/common@2.0.8 - - @latticexyz/config@2.0.8 - - @latticexyz/store@2.0.8 - - @latticexyz/schema-type@2.0.8 - -## 2.0.7 - -### Patch Changes - -- Updated dependencies [375d902e] -- Updated dependencies [38c61158] -- Updated dependencies [ed404b7d] -- Updated dependencies [f736c43d] - - @latticexyz/common@2.0.7 - - @latticexyz/store@2.0.7 - - @latticexyz/config@2.0.7 - - @latticexyz/schema-type@2.0.7 - -## 2.0.6 - -### Patch Changes - -- c18e93c5: Bumped viem to 2.9.20. -- d95028a6: Bumped viem to 2.9.16. -- Updated dependencies [6c8ab471] -- Updated dependencies [103db6ce] -- Updated dependencies [9720b568] -- Updated dependencies [c18e93c5] -- Updated dependencies [d95028a6] - - @latticexyz/common@2.0.6 - - @latticexyz/store@2.0.6 - - @latticexyz/config@2.0.6 - - @latticexyz/schema-type@2.0.6 - -## 2.0.5 - -### Patch Changes - -- Updated dependencies [a9e8a407] -- Updated dependencies [b798ccb2] - - @latticexyz/common@2.0.5 - - @latticexyz/store@2.0.5 - - @latticexyz/config@2.0.5 - - @latticexyz/schema-type@2.0.5 - -## 2.0.4 - -### Patch Changes - -- Updated dependencies [620e4ec1] - - @latticexyz/common@2.0.4 - - @latticexyz/config@2.0.4 - - @latticexyz/store@2.0.4 - - @latticexyz/schema-type@2.0.4 - -## 2.0.3 - -### Patch Changes - -- Updated dependencies [d2e4d0fb] - - @latticexyz/common@2.0.3 - - @latticexyz/config@2.0.3 - - @latticexyz/store@2.0.3 - - @latticexyz/schema-type@2.0.3 - -## 2.0.2 - -### Patch Changes - -- @latticexyz/common@2.0.2 -- @latticexyz/config@2.0.2 -- @latticexyz/schema-type@2.0.2 -- @latticexyz/store@2.0.2 - -## 2.0.1 - -### Patch Changes - -- Updated dependencies [4a6b4598] - - @latticexyz/store@2.0.1 - - @latticexyz/common@2.0.1 - - @latticexyz/config@2.0.1 - - @latticexyz/schema-type@2.0.1 - -## 2.0.0 - -### Patch Changes - -- Updated dependencies [7ce82b6fc] -- Updated dependencies [d8c8f66bf] -- Updated dependencies [c6c13f2ea] -- Updated dependencies [1b86eac05] -- Updated dependencies [a35c05ea9] -- Updated dependencies [c9ee5e4a] -- Updated dependencies [c963b46c7] -- Updated dependencies [05b3e8882] -- Updated dependencies [16b13ea8f] -- Updated dependencies [aea67c580] -- Updated dependencies [82693072] -- Updated dependencies [07dd6f32c] -- Updated dependencies [90e4161bb] -- Updated dependencies [aabd30767] -- Updated dependencies [65c9546c4] -- Updated dependencies [331dbfdcb] -- Updated dependencies [d5c0682fb] -- Updated dependencies [1d60930d6] -- Updated dependencies [01e46d99] -- Updated dependencies [f9f9609ef] -- Updated dependencies [904fd7d4e] -- Updated dependencies [e6c03a87a] -- Updated dependencies [1077c7f53] -- Updated dependencies [2c920de7] -- Updated dependencies [b9e562d8f] -- Updated dependencies [331dbfdcb] -- Updated dependencies [44236041f] -- Updated dependencies [066056154] -- Updated dependencies [759514d8b] -- Updated dependencies [952cd5344] -- Updated dependencies [d5094a242] -- Updated dependencies [3fb9ce283] -- Updated dependencies [bb6ada740] -- Updated dependencies [35c9f33df] -- Updated dependencies [a25881160] -- Updated dependencies [0b8ce3f2c] -- Updated dependencies [933b54b5f] -- Updated dependencies [c4d5eb4e4] -- Updated dependencies [f62c767e7] -- Updated dependencies [9aa5e786] -- Updated dependencies [307abab3] -- Updated dependencies [de151fec0] -- Updated dependencies [37c228c63] -- Updated dependencies [aacffcb59] -- Updated dependencies [c991c71a] -- Updated dependencies [ae340b2bf] -- Updated dependencies [1bf2e9087] -- Updated dependencies [b38c096d] -- Updated dependencies [211be2a1e] -- Updated dependencies [0f3e2e02b] -- Updated dependencies [d08789282] -- Updated dependencies [5c965a919] -- Updated dependencies [f99e88987] -- Updated dependencies [939916bcd] -- Updated dependencies [d5b73b126] -- Updated dependencies [e34d1170] -- Updated dependencies [b8a6158d6] -- Updated dependencies [190fdd11] -- Updated dependencies [433078c54] -- Updated dependencies [db314a74] -- Updated dependencies [b2d2aa715] -- Updated dependencies [83583a505] -- Updated dependencies [5e723b90e] -- Updated dependencies [6573e38e9] -- Updated dependencies [afaf2f5ff] -- Updated dependencies [37c228c63] -- Updated dependencies [59267655] -- Updated dependencies [37c228c63] -- Updated dependencies [44a5432ac] -- Updated dependencies [6e66c5b74] -- Updated dependencies [8d51a0348] -- Updated dependencies [c162ad5a5] -- Updated dependencies [65c9546c4] -- Updated dependencies [48909d151] -- Updated dependencies [7b28d32e5] -- Updated dependencies [b02f9d0e4] -- Updated dependencies [f62c767e7] -- Updated dependencies [bb91edaa0] -- Updated dependencies [590542030] -- Updated dependencies [1b5eb0d07] -- Updated dependencies [44a5432ac] -- Updated dependencies [48c51b52a] -- Updated dependencies [9f8b84e73] -- Updated dependencies [66cc35a8c] -- Updated dependencies [672d05ca1] -- Updated dependencies [55a05fd7a] -- Updated dependencies [f03531d97] -- Updated dependencies [63831a264] -- Updated dependencies [b8a6158d6] -- Updated dependencies [6db95ce15] -- Updated dependencies [8193136a9] -- Updated dependencies [5d737cf2e] -- Updated dependencies [d075f82f3] -- Updated dependencies [331dbfdcb] -- Updated dependencies [a7b30c79b] -- Updated dependencies [92de59982] -- Updated dependencies [22ee44700] -- Updated dependencies [ad4ac4459] -- Updated dependencies [be313068b] -- Updated dependencies [ac508bf18] -- Updated dependencies [93390d89] -- Updated dependencies [bb91edaa0] -- Updated dependencies [144c0d8d] -- Updated dependencies [5ac4c97f4] -- Updated dependencies [bfcb293d1] -- Updated dependencies [3e057061d] -- Updated dependencies [1890f1a06] -- Updated dependencies [e48171741] -- Updated dependencies [9b43029c3] -- Updated dependencies [37c228c63] -- Updated dependencies [55ab88a60] -- Updated dependencies [c58da9ad] -- Updated dependencies [37c228c63] -- Updated dependencies [535229984] -- Updated dependencies [af639a264] -- Updated dependencies [5e723b90e] -- Updated dependencies [99ab9cd6f] -- Updated dependencies [0c4f9fea9] -- Updated dependencies [0d12db8c2] -- Updated dependencies [c049c23f4] -- Updated dependencies [80dd6992e] -- Updated dependencies [60cfd089f] -- Updated dependencies [24a6cd536] -- Updated dependencies [37c228c63] -- Updated dependencies [708b49c50] -- Updated dependencies [d2f8e9400] -- Updated dependencies [25086be5f] -- Updated dependencies [b1d41727d] -- Updated dependencies [3ac68ade6] -- Updated dependencies [22ba7b675] -- Updated dependencies [4c1dcd81e] -- Updated dependencies [3042f86e] -- Updated dependencies [5e71e1cb5] -- Updated dependencies [6071163f7] -- Updated dependencies [6c6733256] -- Updated dependencies [cd5abcc3b] -- Updated dependencies [d7b1c588a] -- Updated dependencies [5c52bee09] -- Updated dependencies [8025c3505] -- Updated dependencies [c4f49240d] -- Updated dependencies [745485cda] -- Updated dependencies [37c228c63] -- Updated dependencies [3e7d83d0] -- Updated dependencies [5df1f31bc] -- Updated dependencies [cea754dde] -- Updated dependencies [331f0d636] -- Updated dependencies [cc2c8da00] -- Updated dependencies [252a1852] -- Updated dependencies [103f635eb] - - @latticexyz/store@2.0.0 - - @latticexyz/common@2.0.0 - - @latticexyz/schema-type@2.0.0 - - @latticexyz/config@2.0.0 - -## 2.0.0-next.18 - -### Patch Changes - -- Updated dependencies [c9ee5e4a] -- Updated dependencies [82693072] -- Updated dependencies [d5c0682fb] -- Updated dependencies [01e46d99] -- Updated dependencies [2c920de7] -- Updated dependencies [44236041] -- Updated dependencies [9aa5e786] -- Updated dependencies [307abab3] -- Updated dependencies [c991c71a] -- Updated dependencies [b38c096d] -- Updated dependencies [e34d1170] -- Updated dependencies [190fdd11] -- Updated dependencies [db314a74] -- Updated dependencies [59267655] -- Updated dependencies [8193136a9] -- Updated dependencies [93390d89] -- Updated dependencies [144c0d8d] -- Updated dependencies [c58da9ad] -- Updated dependencies [3042f86e] -- Updated dependencies [d7b1c588a] -- Updated dependencies [3e7d83d0] -- Updated dependencies [252a1852] - - @latticexyz/store@2.0.0-next.18 - - @latticexyz/common@2.0.0-next.18 - - @latticexyz/schema-type@2.0.0-next.18 - - @latticexyz/config@2.0.0-next.18 diff --git a/packages/query/README.md b/packages/query/README.md deleted file mode 100644 index 023c580b18..0000000000 --- a/packages/query/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# query - -Tools for interacting with the MUD query API. diff --git a/packages/query/package.json b/packages/query/package.json deleted file mode 100644 index 57e6bad646..0000000000 --- a/packages/query/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@latticexyz/query", - "version": "2.2.2", - "description": "Tools for interacting with the MUD query API", - "repository": { - "type": "git", - "url": "https://github.com/latticexyz/mud.git", - "directory": "packages/query" - }, - "license": "MIT", - "type": "module", - "exports": { - ".": "./dist/index.js", - "./internal": "./dist/internal.js" - }, - "typesVersions": { - "*": { - "index": [ - "./dist/index.d.ts" - ], - "internal": [ - "./dist/internal.d.ts" - ] - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsup", - "clean": "shx rm -rf dist", - "dev": "tsup --watch", - "test": "vitest typecheck --run --passWithNoTests && vitest --run --passWithNoTests", - "test:ci": "pnpm run test" - }, - "dependencies": { - "@ark/util": "catalog:", - "@latticexyz/common": "workspace:*", - "@latticexyz/config": "workspace:*", - "@latticexyz/schema-type": "workspace:*", - "@latticexyz/store": "workspace:*", - "viem": "catalog:" - }, - "devDependencies": { - "tsup": "^6.7.0", - "vitest": "0.34.6" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/query/src/api.ts b/packages/query/src/api.ts deleted file mode 100644 index 622f0b15ef..0000000000 --- a/packages/query/src/api.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Hex } from "viem"; -import { satisfy } from "@ark/util"; -import { StaticPrimitiveType, DynamicPrimitiveType, SchemaAbiType } from "@latticexyz/schema-type/internal"; -import { SchemaToPrimitives } from "@latticexyz/store/internal"; -import { Table } from "@latticexyz/config"; - -/** - * These types represent the "over the wire" protocol (i.e. JSON) for the query API. - * - * Currently always returns matching records for each subject. We may add separate endpoints and return types for just subjects later. - */ - -// TODO: decide if we want to support stronger types here (e.g. table generic that constrains subjects, records, etc.) -// TODO: decide if/how we want to add block number throughout (esp as it relates to instant sequencing) -// TODO: separate set of types for querying just - -export type QueryTable = { - readonly tableId: Hex; - readonly field: string; -}; - -export type QuerySubject = { - readonly tableId: Hex; - readonly subject: readonly string[]; -}; - -// TODO: should we exclude arrays? might be hard to support array comparisons in SQL -export type ConditionLiteral = StaticPrimitiveType | DynamicPrimitiveType; - -export type ComparisonCondition = { - readonly left: QueryTable; - readonly op: "<" | "<=" | "=" | ">" | ">=" | "!="; - // TODO: add support for QueryTable - readonly right: ConditionLiteral; -}; - -export type InCondition = { - readonly left: QueryTable; - readonly op: "in"; - readonly right: readonly ConditionLiteral[]; -}; - -export type QueryCondition = satisfy<{ readonly op: string }, ComparisonCondition | InCondition>; - -export type Query = { - readonly from: readonly QuerySubject[]; - readonly except?: readonly QuerySubject[]; - readonly where?: readonly QueryCondition[]; -}; - -export type PrimitiveType = StaticPrimitiveType | DynamicPrimitiveType; - -export type ResultRecord = { - readonly tableId: Hex; - readonly keyTuple: readonly Hex[]; - readonly primaryKey: readonly StaticPrimitiveType[]; - readonly fields: SchemaToPrimitives; -}; - -export type Subject = readonly PrimitiveType[]; -export type SubjectSchema = readonly SchemaAbiType[]; - -export type SubjectRecords = { - readonly subject: Subject; - readonly subjectSchema: SubjectSchema; - readonly records: readonly ResultRecord[]; -}; - -// TODO: consider flattening this to be more like `ResultRecord & { subject: Subject }` -export type SubjectRecord = { - readonly subject: Subject; - readonly subjectSchema: SubjectSchema; - readonly record: ResultRecord; -}; - -// TODO: for change event, should this include previous record? -// TODO: use merge helper instead of `&` intersection? -export type SubjectEvent = SubjectRecord & { - /** - * `enter` = a new subject+record pair matched - * `exit` = a subject+record pair no longer matches - * `change` = the record oft he subject+record pair changed - */ - readonly type: "enter" | "exit" | "change"; -}; - -export type Result = { - readonly subjects: readonly SubjectRecords[]; -}; diff --git a/packages/query/src/common.ts b/packages/query/src/common.ts deleted file mode 100644 index a107a6a1b0..0000000000 --- a/packages/query/src/common.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { StaticPrimitiveType } from "@latticexyz/schema-type/internal"; -import { SchemaToPrimitives } from "@latticexyz/store/internal"; -import { Table } from "@latticexyz/config"; -import { Hex } from "viem"; - -export type TableRecord = { - readonly table: table; - // TODO: refine to just static types - // TODO: add helper to extract primary key of primitive types from table primary key + field values - readonly primaryKey: readonly StaticPrimitiveType[]; - readonly keyTuple: readonly Hex[]; - readonly fields: SchemaToPrimitives; -}; diff --git a/packages/query/src/exports/index.ts b/packages/query/src/exports/index.ts deleted file mode 100644 index 3b94ec7da6..0000000000 --- a/packages/query/src/exports/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * External exports. - * - * Be sure we're ready to commit to these being supported and changes made backward compatible! - */ - -export type { - Query, - QueryCondition, - QuerySubject, - QueryTable, - ConditionLiteral, - InCondition, - ComparisonCondition, - ResultRecord, - Subject, - SubjectSchema, - SubjectRecord, - SubjectRecords, - SubjectEvent, - Result, -} from "../api"; diff --git a/packages/query/src/exports/internal.ts b/packages/query/src/exports/internal.ts deleted file mode 100644 index 5c1ed7149b..0000000000 --- a/packages/query/src/exports/internal.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "../common"; -export * from "../findSubjects"; -export * from "../matchRecords"; diff --git a/packages/query/src/findSubjects.ts b/packages/query/src/findSubjects.ts deleted file mode 100644 index 81243c8661..0000000000 --- a/packages/query/src/findSubjects.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { encodeAbiParameters } from "viem"; -import { Table } from "@latticexyz/config"; -import { groupBy, uniqueBy } from "@latticexyz/common/utils"; -import { Query, SubjectRecords } from "./api"; -import { matchRecords } from "./matchRecords"; -import { TableRecord } from "./common"; - -// This assumes upstream has fully validated query -// This also assumes we have full records, which may not always be the case and we may need some way to request records for a given table subject -// We don't carry around config types here for ease, they get handled by the wrapping `query` function - -export type FindSubjectsParameters
= { - readonly records: readonly TableRecord
[]; - readonly query: Query; -}; - -// TODO: make condition types smarter? so condition literal matches the field primitive type - -export function findSubjects
({ - records: initialRecords, - query, -}: FindSubjectsParameters
): readonly SubjectRecords[] { - const targetTables = Object.fromEntries( - uniqueBy([...query.from, ...(query.except ?? [])], (subject) => subject.tableId).map((subject) => [ - subject.tableId, - subject.subject, - ]), - ); - const fromTableIds = new Set(query.from.map((subject) => subject.tableId)); - const exceptTableIds = new Set((query.except ?? []).map((subject) => subject.tableId)); - - // TODO: store/lookup subjects separately rather than mapping each time so we can "memoize" better? - const records = initialRecords - .filter((record) => targetTables[record.table.tableId]) - .map((record) => { - const subjectFields = targetTables[record.table.tableId]; - const subject = subjectFields.map((field) => record.fields[field]); - const subjectSchema = subjectFields.map((field) => record.table.schema[field]); - const subjectId = encodeAbiParameters(subjectSchema, subject); - return { - ...record, - subjectSchema, - subject, - subjectId, - }; - }); - - const matchedSubjects = Array.from(groupBy(records, (record) => record.subjectId).values()) - .map((records) => ({ - subjectId: records[0].subjectId, - subject: records[0].subject, - subjectSchema: records[0].subjectSchema.map((abiType) => abiType.type), - records, - })) - .filter(({ records }) => { - // make sure our matched subject has no records in `query.except` tables - return exceptTableIds.size ? !records.some((record) => exceptTableIds.has(record.table.tableId)) : true; - }) - .filter(({ records }) => { - // make sure our matched subject has records in all `query.from` tables - const tableIds = new Set(records.map((record) => record.table.tableId)); - return tableIds.size === fromTableIds.size; - }) - .map((match) => { - if (!query.where) return match; - - let records: readonly TableRecord
[] = match.records; - for (const condition of query.where) { - if (!records.length) break; - records = matchRecords(condition, records); - } - - return { ...match, records }; - }) - .filter((match) => match.records.length > 0); - - const subjects = matchedSubjects.map((match) => ({ - subject: match.subject, - subjectSchema: match.subjectSchema, - records: match.records.map((record) => ({ - tableId: record.table.tableId, - primaryKey: record.primaryKey, - keyTuple: record.keyTuple, - fields: record.fields, - })), - })); - - return subjects; -} diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts deleted file mode 100644 index 471b43d6d7..0000000000 --- a/packages/query/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./api"; -export * from "./findSubjects"; -export * from "./matchRecords"; diff --git a/packages/query/src/matchRecords.ts b/packages/query/src/matchRecords.ts deleted file mode 100644 index d7aba8bb5a..0000000000 --- a/packages/query/src/matchRecords.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Table } from "@latticexyz/config"; -import { ComparisonCondition, ConditionLiteral, QueryCondition } from "./api"; -import { TableRecord } from "./common"; - -const comparisons = { - "<": (left, right) => left < right, - "<=": (left, right) => left <= right, - "=": (left, right) => left === right, - ">": (left, right) => left > right, - ">=": (left, right) => left >= right, - "!=": (left, right) => left !== right, -} as const satisfies Record boolean>; - -export function matchRecords
( - condition: QueryCondition, - records: readonly TableRecord
[], -): readonly TableRecord
[] { - switch (condition.op) { - case "<": - case "<=": - case "=": - case ">": - case ">=": - case "!=": - return records.filter( - (record) => - record.table.tableId === condition.left.tableId && - comparisons[condition.op](record.fields[condition.left.field], condition.right), - ); - case "in": - return records.filter( - (record) => - record.table.tableId === condition.left.tableId && - condition.right.includes(record.fields[condition.left.field]), - ); - } -} diff --git a/packages/query/tsconfig.json b/packages/query/tsconfig.json deleted file mode 100644 index 039e0b4d16..0000000000 --- a/packages/query/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src"] -} diff --git a/packages/query/tsup.config.ts b/packages/query/tsup.config.ts deleted file mode 100644 index 709f21cd88..0000000000 --- a/packages/query/tsup.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { defineConfig } from "tsup"; - -export default defineConfig({ - entry: { - index: "src/exports/index.ts", - internal: "src/exports/internal.ts", - }, - target: "esnext", - format: ["esm"], - dts: !process.env.TSUP_SKIP_DTS, - sourcemap: true, - clean: true, - minify: true, -}); diff --git a/packages/store-sync/package.json b/packages/store-sync/package.json index 5112189663..8a0ab2fa1f 100644 --- a/packages/store-sync/package.json +++ b/packages/store-sync/package.json @@ -67,7 +67,6 @@ "@latticexyz/common": "workspace:*", "@latticexyz/config": "workspace:*", "@latticexyz/protocol-parser": "workspace:*", - "@latticexyz/query": "workspace:*", "@latticexyz/recs": "workspace:*", "@latticexyz/schema-type": "workspace:*", "@latticexyz/store": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24daa0b8d8..3d349fb7f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -713,34 +713,6 @@ importers: specifier: 0.34.6 version: 0.34.6(jsdom@22.1.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(terser@5.31.6) - packages/query: - dependencies: - '@ark/util': - specifier: 'catalog:' - version: 0.2.2 - '@latticexyz/common': - specifier: workspace:* - version: link:../common - '@latticexyz/config': - specifier: workspace:* - version: link:../config - '@latticexyz/schema-type': - specifier: workspace:* - version: link:../schema-type - '@latticexyz/store': - specifier: workspace:* - version: link:../store - viem: - specifier: 'catalog:' - version: 2.19.8(bufferutil@4.0.8)(typescript@5.4.2)(utf-8-validate@5.0.10)(zod@3.23.8) - devDependencies: - tsup: - specifier: ^6.7.0 - version: 6.7.0(postcss@8.4.31)(typescript@5.4.2) - vitest: - specifier: 0.34.6 - version: 0.34.6(jsdom@22.1.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(terser@5.31.6) - packages/react: dependencies: '@latticexyz/recs': @@ -1110,9 +1082,6 @@ importers: '@latticexyz/protocol-parser': specifier: workspace:* version: link:../protocol-parser - '@latticexyz/query': - specifier: workspace:* - version: link:../query '@latticexyz/recs': specifier: workspace:* version: link:../recs From 3ca7fc5fb2c06fbfcf87fe7a2e3d6a325a456697 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:23:14 -0700 Subject: [PATCH 10/20] Version Packages (#3132) Co-authored-by: github-actions[bot] --- .changeset/heavy-mice-appear.md | 24 ------------- .changeset/itchy-trees-retire.md | 5 --- .changeset/real-pianos-yell.md | 5 --- CHANGELOG.md | 38 +++++++++++++++++++++ docs/pages/changelog.mdx | 38 +++++++++++++++++++++ packages/abi-ts/CHANGELOG.md | 2 ++ packages/abi-ts/package.json | 2 +- packages/block-logs-stream/CHANGELOG.md | 6 ++++ packages/block-logs-stream/package.json | 2 +- packages/cli/CHANGELOG.md | 37 ++++++++++++++++++++ packages/cli/package.json | 2 +- packages/common/CHANGELOG.md | 6 ++++ packages/common/package.json | 2 +- packages/config/CHANGELOG.md | 7 ++++ packages/config/package.json | 2 +- packages/create-mud/CHANGELOG.md | 2 ++ packages/create-mud/package.json | 2 +- packages/dev-tools/CHANGELOG.md | 14 ++++++++ packages/dev-tools/package.json | 2 +- packages/explorer/CHANGELOG.md | 13 +++++++ packages/explorer/package.json | 2 +- packages/faucet/CHANGELOG.md | 6 ++++ packages/faucet/package.json | 2 +- packages/gas-report/CHANGELOG.md | 2 ++ packages/gas-report/package.json | 2 +- packages/protocol-parser/CHANGELOG.md | 8 +++++ packages/protocol-parser/package.json | 2 +- packages/react/CHANGELOG.md | 7 ++++ packages/react/package.json | 2 +- packages/recs/CHANGELOG.md | 7 ++++ packages/recs/package.json | 2 +- packages/schema-type/CHANGELOG.md | 2 ++ packages/schema-type/package.json | 2 +- packages/solhint-config-mud/CHANGELOG.md | 2 ++ packages/solhint-config-mud/package.json | 2 +- packages/solhint-plugin-mud/CHANGELOG.md | 2 ++ packages/solhint-plugin-mud/package.json | 2 +- packages/stash/CHANGELOG.md | 9 +++++ packages/stash/package.json | 2 +- packages/store-indexer/CHANGELOG.md | 10 ++++++ packages/store-indexer/package.json | 2 +- packages/store-sync/CHANGELOG.md | 14 ++++++++ packages/store-sync/package.json | 2 +- packages/store/CHANGELOG.md | 9 +++++ packages/store/package.json | 2 +- packages/utils/CHANGELOG.md | 2 ++ packages/utils/package.json | 2 +- packages/world-module-metadata/CHANGELOG.md | 9 +++++ packages/world-module-metadata/package.json | 2 +- packages/world-modules/CHANGELOG.md | 11 ++++++ packages/world-modules/package.json | 2 +- packages/world/CHANGELOG.md | 30 ++++++++++++++++ packages/world/package.json | 2 +- test/mock-game-contracts/CHANGELOG.md | 2 ++ test/mock-game-contracts/package.json | 2 +- test/ts-benchmarks/CHANGELOG.md | 2 ++ test/ts-benchmarks/package.json | 2 +- 57 files changed, 323 insertions(+), 60 deletions(-) delete mode 100644 .changeset/heavy-mice-appear.md delete mode 100644 .changeset/itchy-trees-retire.md delete mode 100644 .changeset/real-pianos-yell.md diff --git a/.changeset/heavy-mice-appear.md b/.changeset/heavy-mice-appear.md deleted file mode 100644 index 532c835bcc..0000000000 --- a/.changeset/heavy-mice-appear.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -"@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", - }, -}); -``` diff --git a/.changeset/itchy-trees-retire.md b/.changeset/itchy-trees-retire.md deleted file mode 100644 index 28d31b7d7a..0000000000 --- a/.changeset/itchy-trees-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@latticexyz/explorer": patch ---- - -Fixed an issue with `--worldAddress` CLI flag being incorrectly interpreted as a number rather a hex string. Additionally, added `--hostname` option for specifying the hostname on which to start the application. diff --git a/.changeset/real-pianos-yell.md b/.changeset/real-pianos-yell.md deleted file mode 100644 index 130da6a63b..0000000000 --- a/.changeset/real-pianos-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@latticexyz/cli": patch ---- - -Speed up deployment in development by temporarily enabling automine mode for the duration of the deployment. diff --git a/CHANGELOG.md b/CHANGELOG.md index d73127fd56..445ca00945 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,41 @@ +## Version 2.2.3 + +Release date: Tue Sep 10 2024 + +### Patch changes + +**[feat(cli): deploy custom world (#3131)](https://github.com/latticexyz/mud/commit/854645260c41eaa89cdadad30bf8e70d5d2fd109)** (@latticexyz/cli, @latticexyz/world) + +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", + }, +}); +``` + +**[fix(explorer): world address cli option as hex (#3155)](https://github.com/latticexyz/mud/commit/b9c61a96082e62c4f1bec3a8ebb358ea30c315f0)** (@latticexyz/explorer) + +Fixed an issue with `--worldAddress` CLI flag being incorrectly interpreted as a number rather a hex string. Additionally, added `--hostname` option for specifying the hostname on which to start the application. + +**[feat(cli): speed up dev deploy with temporary automine during deploy (#3130)](https://github.com/latticexyz/mud/commit/d3ab5c3783265b3e82b76157bccedeae6b0445e1)** (@latticexyz/cli) + +Speed up deployment in development by temporarily enabling automine mode for the duration of the deployment. + +--- + ## Version 2.2.2 Release date: Tue Sep 03 2024 diff --git a/docs/pages/changelog.mdx b/docs/pages/changelog.mdx index d73127fd56..445ca00945 100644 --- a/docs/pages/changelog.mdx +++ b/docs/pages/changelog.mdx @@ -1,3 +1,41 @@ +## Version 2.2.3 + +Release date: Tue Sep 10 2024 + +### Patch changes + +**[feat(cli): deploy custom world (#3131)](https://github.com/latticexyz/mud/commit/854645260c41eaa89cdadad30bf8e70d5d2fd109)** (@latticexyz/cli, @latticexyz/world) + +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", + }, +}); +``` + +**[fix(explorer): world address cli option as hex (#3155)](https://github.com/latticexyz/mud/commit/b9c61a96082e62c4f1bec3a8ebb358ea30c315f0)** (@latticexyz/explorer) + +Fixed an issue with `--worldAddress` CLI flag being incorrectly interpreted as a number rather a hex string. Additionally, added `--hostname` option for specifying the hostname on which to start the application. + +**[feat(cli): speed up dev deploy with temporary automine during deploy (#3130)](https://github.com/latticexyz/mud/commit/d3ab5c3783265b3e82b76157bccedeae6b0445e1)** (@latticexyz/cli) + +Speed up deployment in development by temporarily enabling automine mode for the duration of the deployment. + +--- + ## Version 2.2.2 Release date: Tue Sep 03 2024 diff --git a/packages/abi-ts/CHANGELOG.md b/packages/abi-ts/CHANGELOG.md index 469c94b1dd..2f9216f22f 100644 --- a/packages/abi-ts/CHANGELOG.md +++ b/packages/abi-ts/CHANGELOG.md @@ -1,5 +1,7 @@ # @latticexyz/abi-ts +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/packages/abi-ts/package.json b/packages/abi-ts/package.json index 0a02711147..8a8366dbf1 100644 --- a/packages/abi-ts/package.json +++ b/packages/abi-ts/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/abi-ts", - "version": "2.2.2", + "version": "2.2.3", "description": "Create TypeScript type declaration files (`.d.ts`) for your ABI JSON files.", "repository": { "type": "git", diff --git a/packages/block-logs-stream/CHANGELOG.md b/packages/block-logs-stream/CHANGELOG.md index d431168dad..76c5a2a6da 100644 --- a/packages/block-logs-stream/CHANGELOG.md +++ b/packages/block-logs-stream/CHANGELOG.md @@ -1,5 +1,11 @@ # @latticexyz/block-logs-stream +## 2.2.3 + +### Patch Changes + +- @latticexyz/common@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/block-logs-stream/package.json b/packages/block-logs-stream/package.json index 59b54ec96d..49a8c6266f 100644 --- a/packages/block-logs-stream/package.json +++ b/packages/block-logs-stream/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/block-logs-stream", - "version": "2.2.2", + "version": "2.2.3", "description": "Create a stream of EVM block logs for events", "repository": { "type": "git", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 92a84c85f3..09f6a6b4aa 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,42 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- 8546452: 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", + }, + }); + ``` + +- d3ab5c3: Speed up deployment in development by temporarily enabling automine mode for the duration of the deployment. +- Updated dependencies [8546452] + - @latticexyz/world@2.2.3 + - @latticexyz/world-module-metadata@2.2.3 + - @latticexyz/abi-ts@2.2.3 + - @latticexyz/common@2.2.3 + - @latticexyz/config@2.2.3 + - @latticexyz/gas-report@2.2.3 + - @latticexyz/protocol-parser@2.2.3 + - @latticexyz/schema-type@2.2.3 + - @latticexyz/store@2.2.3 + - @latticexyz/utils@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 2fc74a98d3..84a1941283 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/cli", - "version": "2.2.2", + "version": "2.2.3", "description": "Command line interface for mud", "repository": { "type": "git", diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index f3b62eb3bb..6088e1a017 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- @latticexyz/schema-type@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/common/package.json b/packages/common/package.json index d13b2d0335..3adf5cb9f9 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/common", - "version": "2.2.2", + "version": "2.2.3", "description": "Common low level logic shared between packages", "repository": { "type": "git", diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 9d87cf1213..fc2130407d 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- @latticexyz/common@2.2.3 +- @latticexyz/schema-type@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index d2c0a5e7b5..5cfb52a125 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/config", - "version": "2.2.2", + "version": "2.2.3", "description": "Config for Store and World", "repository": { "type": "git", diff --git a/packages/create-mud/CHANGELOG.md b/packages/create-mud/CHANGELOG.md index 38db61548c..3d043b5e96 100644 --- a/packages/create-mud/CHANGELOG.md +++ b/packages/create-mud/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/packages/create-mud/package.json b/packages/create-mud/package.json index 6e7fe0f0de..e9c99e5405 100644 --- a/packages/create-mud/package.json +++ b/packages/create-mud/package.json @@ -1,6 +1,6 @@ { "name": "create-mud", - "version": "2.2.2", + "version": "2.2.3", "description": "Create a new MUD project", "license": "MIT", "author": "Lattice ", diff --git a/packages/dev-tools/CHANGELOG.md b/packages/dev-tools/CHANGELOG.md index 4f9e29b49e..8b75d94354 100644 --- a/packages/dev-tools/CHANGELOG.md +++ b/packages/dev-tools/CHANGELOG.md @@ -1,5 +1,19 @@ # @latticexyz/dev-tools +## 2.2.3 + +### Patch Changes + +- Updated dependencies [8546452] + - @latticexyz/world@2.2.3 + - @latticexyz/store-sync@2.2.3 + - @latticexyz/common@2.2.3 + - @latticexyz/react@2.2.3 + - @latticexyz/recs@2.2.3 + - @latticexyz/schema-type@2.2.3 + - @latticexyz/store@2.2.3 + - @latticexyz/utils@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/dev-tools/package.json b/packages/dev-tools/package.json index 827e94290e..a334c4e906 100644 --- a/packages/dev-tools/package.json +++ b/packages/dev-tools/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/dev-tools", - "version": "2.2.2", + "version": "2.2.3", "description": "MUD developer tools", "repository": { "type": "git", diff --git a/packages/explorer/CHANGELOG.md b/packages/explorer/CHANGELOG.md index ee5a368b3e..81c090117e 100644 --- a/packages/explorer/CHANGELOG.md +++ b/packages/explorer/CHANGELOG.md @@ -1,5 +1,18 @@ # @latticexyz/explorer +## 2.2.3 + +### Patch Changes + +- b9c61a9: Fixed an issue with `--worldAddress` CLI flag being incorrectly interpreted as a number rather a hex string. Additionally, added `--hostname` option for specifying the hostname on which to start the application. +- Updated dependencies [8546452] + - @latticexyz/world@2.2.3 + - @latticexyz/store-sync@2.2.3 + - @latticexyz/common@2.2.3 + - @latticexyz/protocol-parser@2.2.3 + - @latticexyz/schema-type@2.2.3 + - @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/explorer/package.json b/packages/explorer/package.json index f467b3cadb..53f98600e5 100644 --- a/packages/explorer/package.json +++ b/packages/explorer/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/explorer", - "version": "2.2.2", + "version": "2.2.3", "description": "World Explorer is a tool for visually exploring and manipulating the state of worlds", "type": "module", "bin": { diff --git a/packages/faucet/CHANGELOG.md b/packages/faucet/CHANGELOG.md index 02d97ddab4..c56870f8bd 100644 --- a/packages/faucet/CHANGELOG.md +++ b/packages/faucet/CHANGELOG.md @@ -1,5 +1,11 @@ # @latticexyz/faucet +## 2.2.3 + +### Patch Changes + +- @latticexyz/common@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/faucet/package.json b/packages/faucet/package.json index cd06c0f202..515b03e918 100644 --- a/packages/faucet/package.json +++ b/packages/faucet/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/faucet", - "version": "2.2.2", + "version": "2.2.3", "description": "Faucet API for Lattice testnet", "repository": { "type": "git", diff --git a/packages/gas-report/CHANGELOG.md b/packages/gas-report/CHANGELOG.md index 458ee869f0..d8ea4e4302 100644 --- a/packages/gas-report/CHANGELOG.md +++ b/packages/gas-report/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/packages/gas-report/package.json b/packages/gas-report/package.json index 3c7ec4f99c..12a222a623 100644 --- a/packages/gas-report/package.json +++ b/packages/gas-report/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/gas-report", - "version": "2.2.2", + "version": "2.2.3", "description": "Gas reporter for specific lines within forge tests", "repository": { "type": "git", diff --git a/packages/protocol-parser/CHANGELOG.md b/packages/protocol-parser/CHANGELOG.md index 92077d51b1..f7c9e1eb35 100644 --- a/packages/protocol-parser/CHANGELOG.md +++ b/packages/protocol-parser/CHANGELOG.md @@ -1,5 +1,13 @@ # @latticexyz/protocol-parser +## 2.2.3 + +### Patch Changes + +- @latticexyz/common@2.2.3 +- @latticexyz/config@2.2.3 +- @latticexyz/schema-type@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/protocol-parser/package.json b/packages/protocol-parser/package.json index 998a04e16f..5dce1cab8c 100644 --- a/packages/protocol-parser/package.json +++ b/packages/protocol-parser/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/protocol-parser", - "version": "2.2.2", + "version": "2.2.3", "description": "Parser utilities for the MUD protocol", "repository": { "type": "git", diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 7a250ee39d..9884a77d9d 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- @latticexyz/recs@2.2.3 +- @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/react/package.json b/packages/react/package.json index 1404b37938..ebf61c9714 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/react", - "version": "2.2.2", + "version": "2.2.3", "description": "React tools for MUD client.", "repository": { "type": "git", diff --git a/packages/recs/CHANGELOG.md b/packages/recs/CHANGELOG.md index 4d1e5877e6..77f10354f6 100644 --- a/packages/recs/CHANGELOG.md +++ b/packages/recs/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- @latticexyz/schema-type@2.2.3 +- @latticexyz/utils@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/recs/package.json b/packages/recs/package.json index 6fc7bf6b2e..38e4ad03b5 100644 --- a/packages/recs/package.json +++ b/packages/recs/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/recs", - "version": "2.2.2", + "version": "2.2.3", "repository": { "type": "git", "url": "https://github.com/latticexyz/mud.git", diff --git a/packages/schema-type/CHANGELOG.md b/packages/schema-type/CHANGELOG.md index 974ecc5fec..d1ad952a85 100644 --- a/packages/schema-type/CHANGELOG.md +++ b/packages/schema-type/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/packages/schema-type/package.json b/packages/schema-type/package.json index ac1985e8ec..5bf0b07c3b 100644 --- a/packages/schema-type/package.json +++ b/packages/schema-type/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/schema-type", - "version": "2.2.2", + "version": "2.2.3", "description": "SchemaType enum for various languages", "repository": { "type": "git", diff --git a/packages/solhint-config-mud/CHANGELOG.md b/packages/solhint-config-mud/CHANGELOG.md index d91a0d3430..647abda44f 100644 --- a/packages/solhint-config-mud/CHANGELOG.md +++ b/packages/solhint-config-mud/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/packages/solhint-config-mud/package.json b/packages/solhint-config-mud/package.json index 4edb1aa9ce..bb8da2859d 100644 --- a/packages/solhint-config-mud/package.json +++ b/packages/solhint-config-mud/package.json @@ -1,6 +1,6 @@ { "name": "solhint-config-mud", - "version": "2.2.2", + "version": "2.2.3", "repository": { "type": "git", "url": "https://github.com/latticexyz/mud.git", diff --git a/packages/solhint-plugin-mud/CHANGELOG.md b/packages/solhint-plugin-mud/CHANGELOG.md index d91a0d3430..647abda44f 100644 --- a/packages/solhint-plugin-mud/CHANGELOG.md +++ b/packages/solhint-plugin-mud/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/packages/solhint-plugin-mud/package.json b/packages/solhint-plugin-mud/package.json index a426aa976b..cd3ad653a1 100644 --- a/packages/solhint-plugin-mud/package.json +++ b/packages/solhint-plugin-mud/package.json @@ -1,6 +1,6 @@ { "name": "solhint-plugin-mud", - "version": "2.2.2", + "version": "2.2.3", "repository": { "type": "git", "url": "https://github.com/latticexyz/mud.git", diff --git a/packages/stash/CHANGELOG.md b/packages/stash/CHANGELOG.md index 8bf1961c7c..767806fe83 100644 --- a/packages/stash/CHANGELOG.md +++ b/packages/stash/CHANGELOG.md @@ -1,5 +1,14 @@ # @latticexyz/stash +## 2.2.3 + +### Patch Changes + +- @latticexyz/config@2.2.3 +- @latticexyz/protocol-parser@2.2.3 +- @latticexyz/schema-type@2.2.3 +- @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/stash/package.json b/packages/stash/package.json index e43e59e1ab..7bd2e2cb25 100644 --- a/packages/stash/package.json +++ b/packages/stash/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/stash", - "version": "2.2.2", + "version": "2.2.3", "private": true, "description": "High performance client store and query engine for MUD", "repository": { diff --git a/packages/store-indexer/CHANGELOG.md b/packages/store-indexer/CHANGELOG.md index bfdedd94fc..4e69e7761a 100644 --- a/packages/store-indexer/CHANGELOG.md +++ b/packages/store-indexer/CHANGELOG.md @@ -1,5 +1,15 @@ # @latticexyz/store-indexer +## 2.2.3 + +### Patch Changes + +- @latticexyz/store-sync@2.2.3 +- @latticexyz/block-logs-stream@2.2.3 +- @latticexyz/common@2.2.3 +- @latticexyz/protocol-parser@2.2.3 +- @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/store-indexer/package.json b/packages/store-indexer/package.json index dda78c81c4..b5caf65377 100644 --- a/packages/store-indexer/package.json +++ b/packages/store-indexer/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/store-indexer", - "version": "2.2.2", + "version": "2.2.3", "description": "Minimal Typescript indexer for Store", "repository": { "type": "git", diff --git a/packages/store-sync/CHANGELOG.md b/packages/store-sync/CHANGELOG.md index c44f81d8ca..e70078af61 100644 --- a/packages/store-sync/CHANGELOG.md +++ b/packages/store-sync/CHANGELOG.md @@ -1,5 +1,19 @@ # @latticexyz/store-sync +## 2.2.3 + +### Patch Changes + +- Updated dependencies [8546452] + - @latticexyz/world@2.2.3 + - @latticexyz/block-logs-stream@2.2.3 + - @latticexyz/common@2.2.3 + - @latticexyz/config@2.2.3 + - @latticexyz/protocol-parser@2.2.3 + - @latticexyz/recs@2.2.3 + - @latticexyz/schema-type@2.2.3 + - @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/store-sync/package.json b/packages/store-sync/package.json index 8a0ab2fa1f..3e35d9f221 100644 --- a/packages/store-sync/package.json +++ b/packages/store-sync/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/store-sync", - "version": "2.2.2", + "version": "2.2.3", "description": "Utilities to sync MUD Store events with a client or cache", "repository": { "type": "git", diff --git a/packages/store/CHANGELOG.md b/packages/store/CHANGELOG.md index bbc541529c..dd09012b67 100644 --- a/packages/store/CHANGELOG.md +++ b/packages/store/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- @latticexyz/common@2.2.3 +- @latticexyz/config@2.2.3 +- @latticexyz/protocol-parser@2.2.3 +- @latticexyz/schema-type@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/store/package.json b/packages/store/package.json index 1a432882ef..d51a1d186d 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/store", - "version": "2.2.2", + "version": "2.2.3", "description": "Store", "repository": { "type": "git", diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index e57e98bb2f..bb75483435 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/packages/utils/package.json b/packages/utils/package.json index 13b33265aa..85f8dfe863 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/utils", - "version": "2.2.2", + "version": "2.2.3", "repository": { "type": "git", "url": "https://github.com/latticexyz/mud.git", diff --git a/packages/world-module-metadata/CHANGELOG.md b/packages/world-module-metadata/CHANGELOG.md index 9084248adf..00aeef7f76 100644 --- a/packages/world-module-metadata/CHANGELOG.md +++ b/packages/world-module-metadata/CHANGELOG.md @@ -1,5 +1,14 @@ # @latticexyz/world-module-metadata +## 2.2.3 + +### Patch Changes + +- Updated dependencies [8546452] + - @latticexyz/world@2.2.3 + - @latticexyz/schema-type@2.2.3 + - @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/world-module-metadata/package.json b/packages/world-module-metadata/package.json index f77ece4ac8..d391fefbbc 100644 --- a/packages/world-module-metadata/package.json +++ b/packages/world-module-metadata/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/world-module-metadata", - "version": "2.2.2", + "version": "2.2.3", "description": "Metadata world module", "repository": { "type": "git", diff --git a/packages/world-modules/CHANGELOG.md b/packages/world-modules/CHANGELOG.md index 262b9c97e4..74083fbe4d 100644 --- a/packages/world-modules/CHANGELOG.md +++ b/packages/world-modules/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- Updated dependencies [8546452] + - @latticexyz/world@2.2.3 + - @latticexyz/common@2.2.3 + - @latticexyz/config@2.2.3 + - @latticexyz/schema-type@2.2.3 + - @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/world-modules/package.json b/packages/world-modules/package.json index 9b9534800d..b34cd47536 100644 --- a/packages/world-modules/package.json +++ b/packages/world-modules/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/world-modules", - "version": "2.2.2", + "version": "2.2.3", "description": "World modules", "repository": { "type": "git", diff --git a/packages/world/CHANGELOG.md b/packages/world/CHANGELOG.md index 6ffee1ecff..50f0c710d2 100644 --- a/packages/world/CHANGELOG.md +++ b/packages/world/CHANGELOG.md @@ -1,5 +1,35 @@ # Change Log +## 2.2.3 + +### Patch Changes + +- 8546452: 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", + }, + }); + ``` + + - @latticexyz/common@2.2.3 + - @latticexyz/config@2.2.3 + - @latticexyz/protocol-parser@2.2.3 + - @latticexyz/schema-type@2.2.3 + - @latticexyz/store@2.2.3 + ## 2.2.2 ### Patch Changes diff --git a/packages/world/package.json b/packages/world/package.json index 050d51e6ca..946faa2900 100644 --- a/packages/world/package.json +++ b/packages/world/package.json @@ -1,6 +1,6 @@ { "name": "@latticexyz/world", - "version": "2.2.2", + "version": "2.2.3", "description": "World framework", "repository": { "type": "git", diff --git a/test/mock-game-contracts/CHANGELOG.md b/test/mock-game-contracts/CHANGELOG.md index e2649c5442..930dad5b36 100644 --- a/test/mock-game-contracts/CHANGELOG.md +++ b/test/mock-game-contracts/CHANGELOG.md @@ -1,5 +1,7 @@ # mock-game-contracts +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/test/mock-game-contracts/package.json b/test/mock-game-contracts/package.json index 6ee9ec93ee..2e4a229aef 100644 --- a/test/mock-game-contracts/package.json +++ b/test/mock-game-contracts/package.json @@ -1,6 +1,6 @@ { "name": "mock-game-contracts", - "version": "2.2.2", + "version": "2.2.3", "private": true, "license": "MIT", "scripts": { diff --git a/test/ts-benchmarks/CHANGELOG.md b/test/ts-benchmarks/CHANGELOG.md index 42745cd611..4cc4d030eb 100644 --- a/test/ts-benchmarks/CHANGELOG.md +++ b/test/ts-benchmarks/CHANGELOG.md @@ -1,5 +1,7 @@ # ts-benchmarks +## 2.2.3 + ## 2.2.2 ## 2.2.1 diff --git a/test/ts-benchmarks/package.json b/test/ts-benchmarks/package.json index 88d0d97d47..5f34c8f013 100644 --- a/test/ts-benchmarks/package.json +++ b/test/ts-benchmarks/package.json @@ -1,6 +1,6 @@ { "name": "ts-benchmarks", - "version": "2.2.2", + "version": "2.2.3", "private": true, "license": "MIT", "scripts": { From 03a2e97078b40ab585bd78611985551684f01289 Mon Sep 17 00:00:00 2001 From: Karolis Ramanauskas Date: Tue, 10 Sep 2024 21:26:36 +0300 Subject: [PATCH 11/20] docs(explorer): add hostname cli option (#3161) --- docs/pages/world-explorer.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/world-explorer.mdx b/docs/pages/world-explorer.mdx index 18791e1730..d576046a57 100644 --- a/docs/pages/world-explorer.mdx +++ b/docs/pages/world-explorer.mdx @@ -58,6 +58,7 @@ The World Explorer accepts the following CLI arguments: | `indexerDatabase` | Path to your SQLite indexer database | "indexer.db" | | `chainId` | The chain ID of the network | 31337 | | `port` | The port on which to run the World Explorer | 13690 | +| `hostname` | The host on which to run the World Explorer | 0.0.0.0 | ## Installation From bc7134eff50334ceb30ce1cac4fc1feee7ef94af Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 11 Sep 2024 03:11:47 -0500 Subject: [PATCH 12/20] docs(guide/hello-world): deploy guide (#3168) --- docs/pages/guides/hello-world.mdx | 2 +- docs/pages/guides/hello-world/_meta.js | 5 +-- docs/pages/guides/hello-world/deploy.mdx | 57 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 docs/pages/guides/hello-world/deploy.mdx diff --git a/docs/pages/guides/hello-world.mdx b/docs/pages/guides/hello-world.mdx index 263cede440..86de44764c 100644 --- a/docs/pages/guides/hello-world.mdx +++ b/docs/pages/guides/hello-world.mdx @@ -11,4 +11,4 @@ The exception is filtering data synchronization, because it needs a table that i - [Add a table](hello-world/add-table) - [Filter data synchronization](hello-world/filter-sync) - [Add a system](hello-world/add-system) -- [Deploy to a blockchain](../cli/deploy) +- [Deploy to a blockchain](hello-world/deploy) diff --git a/docs/pages/guides/hello-world/_meta.js b/docs/pages/guides/hello-world/_meta.js index 8c4b16f604..a14157d2bd 100644 --- a/docs/pages/guides/hello-world/_meta.js +++ b/docs/pages/guides/hello-world/_meta.js @@ -2,9 +2,6 @@ export default { "add-table": "Add a table", "filter-sync": "Filter data synchronization", "add-system": "Add a system", - "deploy": { - "title": "Deploy to a blockchain", - "href": "/cli/deploy" - }, + "deploy": "Deploy to a blockchain", "add-chain-client": "Add chains to the client", }; diff --git a/docs/pages/guides/hello-world/deploy.mdx b/docs/pages/guides/hello-world/deploy.mdx new file mode 100644 index 0000000000..0e5a2687fa --- /dev/null +++ b/docs/pages/guides/hello-world/deploy.mdx @@ -0,0 +1,57 @@ +# Deploy to a blockchain + +In this tutorial you deploy a MUD application to our test blockchain, [Garnet](https://garnetchain.com/bridge) + +## Setup + +[Create a new MUD application from the template](/quickstart). +Use the `vanilla` template. + +```sh copy +pnpm create mud@latest tutorial --template vanilla +cd tutorial +pnpm dev +``` + +## Deployment + +1. Change to the `contracts` package. + + ```sh copy + cd packages/contracts + ``` + +1. See that `foundry.toml` has a `[profile.garnet]` section with the RPC URL. + +1. Connect to [the Garnet faucet](https://garnetchain.com/faucet) to obtain test ETH. + +1. Specify the private key for the address that got the test ETH on Garnet. + + ```sh copy + export PRIVATE_KEY=0x + ``` + +1. Deploy using the [`mud deploy`](/cli/deploy) command. + + ```sh copy + pnpm mud deploy --profile garnet + ``` + +1. After the deployment is over, look at the `World` information. + The key, `17069`, is the chain ID for Garnet. + + ```sh copy + cat worlds.json | jq .\"17069\" + ``` + +1. To go to your application, [specify the chain ID on the URL](http://localhost:3000/?chainid=17069). + You can also quit out of the `pnpm dev` and specify the chain ID when running vite. + + ```sh copy + cd packages/client + export VITE_CHAIN_ID=17069 + pnpm vite + ``` + + When you do that, it is no longer necessary to specify the chain ID on the URL. + [The default URL](http://localhost:3000) takes you directly to the onchain application. From b252618ca77bcd364f06bd2037e9040a39aeaec5 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 11 Sep 2024 03:13:36 -0500 Subject: [PATCH 13/20] =?UTF-8?q?docs(indexer/sqlite):=20add=20`PORT`=20en?= =?UTF-8?q?v=20variable=20=F0=9F=9A=97=20=20(#3167)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/pages/indexer/postgres-event-only.mdx | 1 + docs/pages/indexer/sqlite.mdx | 1 + docs/pages/services/_meta.js | 4 ---- 3 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 docs/pages/services/_meta.js diff --git a/docs/pages/indexer/postgres-event-only.mdx b/docs/pages/indexer/postgres-event-only.mdx index d9db10d6a3..74a0a1eb4a 100644 --- a/docs/pages/indexer/postgres-event-only.mdx +++ b/docs/pages/indexer/postgres-event-only.mdx @@ -17,6 +17,7 @@ These environment variables need to be provided to the indexer to work: | Optional | DEBUG=mud:\* | Turn on debugging | | | Optional | STORE_ADDRESS | Only index tables from this `World` | | Required | DATABASE_URL | URL for the database, of the form `postgres:///` | +| Optional | PORT | The port on which the indexer listens | `3001` (the default) | ### Using npx diff --git a/docs/pages/indexer/sqlite.mdx b/docs/pages/indexer/sqlite.mdx index 56168b6133..0f2800e01d 100644 --- a/docs/pages/indexer/sqlite.mdx +++ b/docs/pages/indexer/sqlite.mdx @@ -17,6 +17,7 @@ These environment variables need to be provided to the indexer to work: | Optional | DEBUG=mud:\* | Turn on debugging | | | Optional | STORE_ADDRESS | Only index tables from this `World` | | Optional | SQLITE_FILENAME | Name of database | `indexer.db` | +| Optional | PORT | The port on which the indexer listens | `3001` (the default) | ### Using npx diff --git a/docs/pages/services/_meta.js b/docs/pages/services/_meta.js deleted file mode 100644 index 6e5616afa1..0000000000 --- a/docs/pages/services/_meta.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - indexer: "Indexer", - faucet: "Faucet", -}; From e9c75945b39759db51c6d3da69e6779280effde8 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Wed, 11 Sep 2024 03:15:30 -0500 Subject: [PATCH 14/20] docs(world/modules/erc20): first version (#3166) --- docs/pages/world/modules/_meta.js | 1 + docs/pages/world/modules/erc20.mdx | 314 +++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 docs/pages/world/modules/erc20.mdx diff --git a/docs/pages/world/modules/_meta.js b/docs/pages/world/modules/_meta.js index 0c5379cd01..de7612a3b3 100644 --- a/docs/pages/world/modules/_meta.js +++ b/docs/pages/world/modules/_meta.js @@ -1,5 +1,6 @@ export default { "keyswithvalue": "Keys with Value", "keysintable": "Keys in Table", + "erc20": "ERC-20 tokens", "erc721": "ERC-721 (NFT)" }; diff --git a/docs/pages/world/modules/erc20.mdx b/docs/pages/world/modules/erc20.mdx new file mode 100644 index 0000000000..1bd906b91c --- /dev/null +++ b/docs/pages/world/modules/erc20.mdx @@ -0,0 +1,314 @@ +import { CollapseCode } from "../../../components/CollapseCode"; +import { Callout } from "nextra/components"; + +# ERC 20 (fungible tokens) module + + + +This module is unaudited and may change in the future. + + + +The [`erc20-puppet`](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/erc20-puppet) module lets you create [ERC-20](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) tokens as part of a MUD `World`. +The advantage of doing this, rather than creating a separate [ERC-20 contract](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20) and merely controlling it from MUD, is that all the information is in MUD tables and is immediately available in the client. + +## Deployment + +The easiest way to deploy this module is to edit `mud.config.ts`. +This is a modified version of the [vanilla](/templates/typescript/contracts) template. + +Note that before you use this file you need to run `pnpm add viem` (see explanation below). + + + +```typescript filename="mud.config.ts" showLineNumbers copy {2-13,25-41} +import { defineWorld } from "@latticexyz/world"; +import { encodeAbiParameters, stringToHex } from "viem"; + +const erc20ModuleArgs = encodeAbiParameters( + [ + { type: "bytes14" }, + { + type: "tuple", + components: [{ type: "uint8" }, { type: "string" }, { type: "string" }], + }, + ], + [stringToHex("MyToken", { size: 14 }), [18, "Worthless Token", "WT"]], +); + +export default defineWorld({ + namespace: "app", + tables: { + Counter: { + schema: { + value: "uint32", + }, + key: [], + }, + }, + modules: [ + { + artifactPath: "@latticexyz/world-modules/out/PuppetModule.sol/PuppetModule.json", + root: false, + args: [], + }, + { + artifactPath: "@latticexyz/world-modules/out/ERC20Module.sol/ERC20Module.json", + root: false, + args: [ + { + type: "bytes", + value: erc20ModuleArgs, + }, + ], + }, + ], +}); +``` + + + +
+ +Explanation + +```typescript +import { encodeAbiParameters, stringToHex } from "viem"; +``` + +In simple cases it is enough to use the config parser to specify the module arguments. +However, the ERC-20 module requires a `struct` as [one of the arguments](https://github.com/latticexyz/mud/blob/main/packages/world-modules/src/modules/erc20-puppet/ERC20Module.sol#L34). +We use [`encodeAbiParameters`](https://viem.sh/docs/abi/encodeAbiParameters.html) to encode the `struct` data. +The [`stringToHex`](https://viem.sh/docs/utilities/toHex.html#stringtohex) function is used to specify the namespace the token uses. + +This is the reason we need to issue `pnpm install viem` in `packages/contracts` to be able to use the library here. + +```typescript +const erc20ModuleArgs = encodeAbiParameters( +``` + +You can see the arguments for the ERC-20 module [here](https://github.com/latticexyz/mud/blob/main/packages/world-modules/src/modules/erc20-puppet/ERC20Module.sol#L34). +There are two arguments: + +- A 14-byte identifier for the namespace. +- An `ERC20MetadataData` for the ERC-20 parameters, [defined here](https://github.com/latticexyz/mud/blob/main/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol#L19-L23). + +However, the arguments for a module are [ABI encoded](https://docs.soliditylang.org/en/develop/abi-spec.html) to a single value of type `bytes`. +So we use `encodeAbiParameters` from the viem library to create this argument. +The first parameter of this function is a list of argument types. + +```typescript + [ + { type: "bytes14" }, +``` + +The first parameter is simple, a 14 byte value for the namespace. + +```typescript + { + type: "tuple", + components: [{ type: "uint8" }, { type: "string" }, { type: "string" }], + }, +``` + +The second value is more complicated, it's a struct, or as it is called in ABI, a tuple. +The first field is the number of digits after the decimal point when displaying the token. +The second field is the token's full name, and the third a short symbol for it. + +```typescript + [ + stringToHex("MyToken", { size: 14 }), +``` + +The second `encodeAbiParameters` parameter is a list of the values, of the types declared in the first list. + +The first parameter for the module is `bytes14`, the namespace of the ERC-20 token. +We use [`stringToHex`](https://viem.sh/docs/utilities/toHex.html#stringtohex) to convert it from the text form that is easy for us to use, to the hexadecimal number that Viem expects for `bytes14` parameter. + +```typescript + [18, "Worthless Token", "WT"]], + ], +); +``` + +The second parameter for the module is the [`ERC20MetadataData`](https://github.com/latticexyz/mud/blob/main/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol#L19-L23) structure. + +```typesceript + modules: [ + { + artifactPath: "@latticexyz/world-modules/out/PuppetModule.sol/PuppetModule.json", + root: false, + args: [], + }, +``` + +A module declaration requires three parameters: + +- `artifactPath`, a link to the compiled JSON file for the module. +- `root`, whether to install the module with [root namespace permissions](/world/systems#root-systems) or not. +- `args` the module arguments. + +Here we install [the `puppet` module](https://github.com/latticexyz/mud/tree/main/packages/world-modules/src/modules/puppet). +We need this module because a `System` is supposed to be stateless, and easily upgradeable to a contract in a different address. +However, both the [ERC-20 standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) and the [ERC-721 standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-721/) require the token contract to emit events. +The solution is to put the `System` in one contract and have another contract, the puppet, which receives requests and emits events according to the ERC. + +```typescript + { + artifactPath: "@latticexyz/world-modules/out/ERC20Module.sol/ERC20Module.json", + root: false, + args: [ + { + type: "bytes", +``` + +The data type for this parameter is `bytes`, because it is treated as opaque bytes by the `World` and only gets parsed by the module after it is transferred. + +```typescript + value: erc20ModuleArgs, + }, + ], + }, +``` + +The module arguments, stored in `erc20ModuleArgs`. + +
+ +## Usage + +You can use the token the same way you use any other ERC20 contract. +For example, run this script. + + + +```solidity filename="ManageERC20.s.sol" copy showLineNumbers {16,35-39,45,50-64} +import { console } from "forge-std/console.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { RESOURCE_TABLE } from "@latticexyz/store/src/storeResourceTypes.sol"; +import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; +import { ERC20Registry } from "@latticexyz/world-modules/src/codegen/index.sol"; +import { IERC20Mintable } from "@latticexyz/world-modules/src/modules/erc20-puppet/IERC20Mintable.sol"; + +import { IWorld } from "../src/codegen/world/IWorld.sol"; + +contract ManageERC20 is Script { + function reportBalances(IERC20Mintable erc20, address myAddress) internal view { + address goodGuy = address(0x600D); + address badGuy = address(0x0BAD); + + console.log(" My balance:", erc20.balanceOf(myAddress)); + console.log("Goodguy balance:", erc20.balanceOf(goodGuy)); + console.log(" Badguy balance:", erc20.balanceOf(badGuy)); + console.log("--------------"); + } + + function run() external { + address worldAddress = address(0x8D8b6b8414E1e3DcfD4168561b9be6bD3bF6eC4B); + + // 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"); + address myAddress = vm.addr(deployerPrivateKey); + + // Start broadcasting transactions from the deployer account + vm.startBroadcast(deployerPrivateKey); + + // Get the ERC-20 token address + ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("MyToken")); + ResourceId erc20RegistryResource = WorldResourceIdLib.encode(RESOURCE_TABLE, "erc20-puppet", "ERC20Registry"); + address tokenAddress = ERC20Registry.getTokenAddress(erc20RegistryResource, namespaceResource); + console.log("Token address", tokenAddress); + + address goodGuy = address(0x600D); + address badGuy = address(0x0BAD); + + // Use the token + IERC20Mintable erc20 = IERC20Mintable(tokenAddress); + + console.log("Initial state"); + reportBalances(erc20, myAddress); + + // Mint some tokens + console.log("Minting for myself and Badguy"); + erc20.mint(myAddress, 1000); + erc20.mint(badGuy, 500); + reportBalances(erc20, myAddress); + + // Transfer tokens + console.log("Transfering to Goodguy"); + erc20.transfer(goodGuy, 750); + reportBalances(erc20, myAddress); + + // Burn tokens + console.log("Burning badGuy's tokens"); + erc20.burn(badGuy, 500); + reportBalances(erc20, myAddress); + + vm.stopBroadcast(); + } +} +``` + + + +
+ +Explanation + +```solidity + console.log(" My balance:", erc20.balanceOf(myAddress)); +``` + +[The `balanceOf` function](https://eips.ethereum.org/EIPS/eip-20#balanceof) is the way ERC-20 specifies to get an address's balance. + +```solidity + // Get the ERC-20 token address + ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("MyToken")); + ResourceId erc20RegistryResource = WorldResourceIdLib.encode(RESOURCE_TABLE, "erc20-puppet", "ERC20Registry"); + address tokenAddress = ERC20Registry.getTokenAddress(erc20RegistryResource, namespaceResource); + console.log("Token address", tokenAddress); +``` + +This is the process to get the address of our token contract (the puppet). +First, we get the [`resourceId` values](/world/resource-ids) for the `erc20-puppet__ERC20Registry` table and the namespace we are interested in (each namespace can only have one ERC-20 token). +Then we use that table to get the token address. + +```solidity + // Use the token + IERC20Mintable erc20 = IERC20Mintable(tokenAddress); +``` + +Create an [`IERC20Mintable`](https://github.com/latticexyz/mud/blob/main/packages/world-modules/src/modules/erc20-puppet/IERC20Mintable.sol) for the token. + +```solidity + console.log("Minting for myself and Badguy"); + erc20.mint(myAddress, 1000); + erc20.mint(badGuy, 500); + reportBalances(erc20, myAddress); +``` + +Mint tokens for two addresses. +Note that only the owner of the name space is authorized to mint tokens. + +```solidity + console.log("Transfering to Goodguy"); + erc20.transfer(goodGuy, 750); + reportBalances(erc20, myAddress); +``` + +Transfer a token. +We can only transfer tokens we own, or that we have approval to transfer from the current owner. + +```solidity + console.log("Burning badGuy's tokens"); + erc20.burn(badGuy, 500); + reportBalances(erc20, myAddress); +``` + +Destroy some tokens. + +
From d3acd9242da44d201ea99e04c1631ed687d30a80 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Thu, 12 Sep 2024 09:27:35 +0000 Subject: [PATCH 15/20] feat(cli): register namespace labels (#3172) --- .changeset/metal-brooms-enjoy.md | 5 +++++ packages/cli/src/deploy/deploy.ts | 28 +++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 .changeset/metal-brooms-enjoy.md diff --git a/.changeset/metal-brooms-enjoy.md b/.changeset/metal-brooms-enjoy.md new file mode 100644 index 0000000000..8128206ed7 --- /dev/null +++ b/.changeset/metal-brooms-enjoy.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/cli": patch +--- + +Along with table and system labels, the MUD deployer now registers namespace labels. Additionally, labels will only be registered if they differ from the underlying resource name. diff --git a/packages/cli/src/deploy/deploy.ts b/packages/cli/src/deploy/deploy.ts index 610b8634ce..bbd13d5beb 100644 --- a/packages/cli/src/deploy/deploy.ts +++ b/packages/cli/src/deploy/deploy.ts @@ -9,7 +9,7 @@ import { ensureFunctions } from "./ensureFunctions"; import { ensureModules } from "./ensureModules"; import { ensureNamespaceOwner } from "./ensureNamespaceOwner"; import { debug } from "./debug"; -import { resourceToLabel } from "@latticexyz/common"; +import { resourceToHex, resourceToLabel } from "@latticexyz/common"; import { ensureContractsDeployed } from "./ensureContractsDeployed"; import { randomBytes } from "crypto"; import { ensureWorldFactory } from "./ensureWorldFactory"; @@ -19,6 +19,7 @@ import { waitForTransactions } from "./waitForTransactions"; import { ContractArtifact } from "@latticexyz/world/node"; import { World } from "@latticexyz/world"; import { deployCustomWorld } from "./deployCustomWorld"; +import { uniqueBy } from "@latticexyz/common/utils"; type DeployOptions = { config: World; @@ -150,9 +151,26 @@ export async function deploy({ modules, }); - const tableTags = tables.map(({ tableId: resourceId, label }) => ({ resourceId, tag: "label", value: label })); - const systemTags = systems.flatMap(({ systemId: resourceId, label, abi, worldAbi }) => [ - { resourceId, tag: "label", value: label }, + const namespaceTags = uniqueBy( + [...tables, ...systems] + // only register labels if they differ from the resource ID + .filter(({ namespace, namespaceLabel }) => namespaceLabel !== namespace) + .map(({ namespace, namespaceLabel }) => ({ + resourceId: resourceToHex({ type: "namespace", namespace, name: "" }), + tag: "label", + value: namespaceLabel, + })), + (tag) => tag.resourceId, + ); + + const tableTags = tables + // only register labels if they differ from the resource ID + .filter((table) => table.label !== table.name) + .map(({ tableId: resourceId, label }) => ({ resourceId, tag: "label", value: label })); + + const systemTags = systems.flatMap(({ name, systemId: resourceId, label, abi, worldAbi }) => [ + // only register labels if they differ from the resource ID + ...(label !== name ? [{ resourceId, tag: "label", value: label }] : []), { resourceId, tag: "abi", value: abi.join("\n") }, { resourceId, tag: "worldAbi", value: worldAbi.join("\n") }, ]); @@ -162,7 +180,7 @@ export async function deploy({ deployerAddress, libraries, worldDeploy, - tags: [...tableTags, ...systemTags], + tags: [...namespaceTags, ...tableTags, ...systemTags], valueToHex: stringToHex, }); From 784e5a98e679388ad6bc941cd1bc9b6486cf276d Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Thu, 12 Sep 2024 14:55:40 +0000 Subject: [PATCH 16/20] feat(explorer): write observer (#3169) Co-authored-by: Karolis Ramanauskas --- .changeset/smart-parents-refuse.md | 33 ++++++ .../packages/client/package.json | 2 +- .../packages/client/src/index.tsx | 17 --- .../packages/client/src/mud/setupNetwork.ts | 21 ++-- examples/local-explorer/pnpm-lock.yaml | 6 +- package.json | 2 +- packages/explorer/bin/explorer.js | 3 + packages/explorer/package.json | 30 +++-- .../explorer/src/app/(explorer)/error.tsx | 2 +- .../explorer/src/app/(explorer)/not-found.tsx | 2 +- .../{explorer => explore}/DataExplorer.tsx | 0 .../EditableTableCell.tsx | 0 .../{explorer => explore}/TableSelector.tsx | 0 .../{explorer => explore}/TablesViewer.tsx | 0 .../{explorer => explore}/page.tsx | 0 .../worlds/[worldAddress]/observe/Write.tsx | 31 +++++ .../worlds/[worldAddress]/observe/Writes.tsx | 26 ++++ .../worlds/[worldAddress]/observe/common.ts | 1 + .../worlds/[worldAddress]/observe/page.tsx | 5 + .../(explorer)/worlds/[worldAddress]/page.tsx | 2 +- packages/explorer/src/app/internal/layout.tsx | 7 ++ .../src/app/internal/observer-relay/Relay.tsx | 9 ++ .../src/app/internal/observer-relay/page.tsx | 5 + packages/explorer/{ => src}/bin/explorer.ts | 5 +- .../explorer/src/components/KeepInView.tsx | 38 ++++++ .../explorer/src/components/Navigation.tsx | 15 ++- packages/explorer/src/debug.ts | 3 + packages/explorer/src/exports/observer.ts | 3 + packages/explorer/src/observer/README.md | 23 ++++ packages/explorer/src/observer/bridge.ts | 81 +++++++++++++ packages/explorer/src/observer/common.ts | 5 + packages/explorer/src/observer/debug.ts | 3 + packages/explorer/src/observer/decorator.ts | 72 +++++++++++ packages/explorer/src/observer/messages.ts | 37 ++++++ packages/explorer/src/observer/relay.ts | 20 ++++ packages/explorer/src/observer/store.ts | 44 +++++++ packages/explorer/tsconfig.tsup.json | 3 + packages/explorer/tsup.config.ts | 6 +- pnpm-lock.yaml | 112 ++++++++++++------ 39 files changed, 590 insertions(+), 84 deletions(-) create mode 100644 .changeset/smart-parents-refuse.md create mode 100755 packages/explorer/bin/explorer.js rename packages/explorer/src/app/(explorer)/worlds/[worldAddress]/{explorer => explore}/DataExplorer.tsx (100%) rename packages/explorer/src/app/(explorer)/worlds/[worldAddress]/{explorer => explore}/EditableTableCell.tsx (100%) rename packages/explorer/src/app/(explorer)/worlds/[worldAddress]/{explorer => explore}/TableSelector.tsx (100%) rename packages/explorer/src/app/(explorer)/worlds/[worldAddress]/{explorer => explore}/TablesViewer.tsx (100%) rename packages/explorer/src/app/(explorer)/worlds/[worldAddress]/{explorer => explore}/page.tsx (100%) create mode 100644 packages/explorer/src/app/(explorer)/worlds/[worldAddress]/observe/Write.tsx create mode 100644 packages/explorer/src/app/(explorer)/worlds/[worldAddress]/observe/Writes.tsx create mode 100644 packages/explorer/src/app/(explorer)/worlds/[worldAddress]/observe/common.ts create mode 100644 packages/explorer/src/app/(explorer)/worlds/[worldAddress]/observe/page.tsx create mode 100644 packages/explorer/src/app/internal/layout.tsx create mode 100644 packages/explorer/src/app/internal/observer-relay/Relay.tsx create mode 100644 packages/explorer/src/app/internal/observer-relay/page.tsx rename packages/explorer/{ => src}/bin/explorer.ts (97%) create mode 100644 packages/explorer/src/components/KeepInView.tsx create mode 100644 packages/explorer/src/debug.ts create mode 100644 packages/explorer/src/exports/observer.ts create mode 100644 packages/explorer/src/observer/README.md create mode 100644 packages/explorer/src/observer/bridge.ts create mode 100644 packages/explorer/src/observer/common.ts create mode 100644 packages/explorer/src/observer/debug.ts create mode 100644 packages/explorer/src/observer/decorator.ts create mode 100644 packages/explorer/src/observer/messages.ts create mode 100644 packages/explorer/src/observer/relay.ts create mode 100644 packages/explorer/src/observer/store.ts create mode 100644 packages/explorer/tsconfig.tsup.json diff --git a/.changeset/smart-parents-refuse.md b/.changeset/smart-parents-refuse.md new file mode 100644 index 0000000000..bf160770f5 --- /dev/null +++ b/.changeset/smart-parents-refuse.md @@ -0,0 +1,33 @@ +--- +"@latticexyz/explorer": patch +--- + +World Explorer package now exports an `observer` Viem decorator that can be used to get visibility into contract writes initiated from your app. You can watch these writes stream in on the new "Observe" tab of the World Explorer. + +```ts +import { createClient, publicActions, walletActions } from "viem"; +import { observer } from "@latticexyz/explorer/observer"; + +const client = createClient({ ... }) + .extend(publicActions) + .extend(walletActions) + .extend(observer()); +``` + +By default, the `observer` action assumes the World Explorer is running at `http://localhost:13690`, but this can be customized with the `explorerUrl` option. + +```ts +observer({ + explorerUrl: "http://localhost:4444", +}); +``` + +If you want to measure the timing of transaction-to-state-change, you can also pass in a `waitForStateChange` function that takes a transaction hash and returns a partial [`TransactionReceipt`](https://viem.sh/docs/glossary/types#transactionreceipt) with `blockNumber`, `status`, and `transactionHash`. This mirrors the `waitForTransaction` function signature returned by `syncTo...` helper in `@latticexyz/store-sync`. + +```ts +observer({ + async waitForStateChange(hash) { + return await waitForTransaction(hash); + }, +}); +``` diff --git a/examples/local-explorer/packages/client/package.json b/examples/local-explorer/packages/client/package.json index cb8f88f0b0..8dbe1b0f80 100644 --- a/examples/local-explorer/packages/client/package.json +++ b/examples/local-explorer/packages/client/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@latticexyz/common": "link:../../../../packages/common", - "@latticexyz/dev-tools": "link:../../../../packages/dev-tools", + "@latticexyz/explorer": "link:../../../../packages/explorer", "@latticexyz/react": "link:../../../../packages/react", "@latticexyz/schema-type": "link:../../../../packages/schema-type", "@latticexyz/store-sync": "link:../../../../packages/store-sync", diff --git a/examples/local-explorer/packages/client/src/index.tsx b/examples/local-explorer/packages/client/src/index.tsx index c9b662c9f0..5362fcf0a4 100644 --- a/examples/local-explorer/packages/client/src/index.tsx +++ b/examples/local-explorer/packages/client/src/index.tsx @@ -2,7 +2,6 @@ import ReactDOM from "react-dom/client"; import { App } from "./App"; import { setup } from "./mud/setup"; import { MUDProvider } from "./MUDContext"; -import mudConfig from "contracts/mud.config"; const rootElement = document.getElementById("react-root"); if (!rootElement) throw new Error("React root not found"); @@ -15,20 +14,4 @@ setup().then(async (result) => { , ); - - // https://vitejs.dev/guide/env-and-mode.html - if (import.meta.env.DEV) { - const { mount: mountDevTools } = await import("@latticexyz/dev-tools"); - mountDevTools({ - config: mudConfig, - publicClient: result.network.publicClient, - walletClient: result.network.walletClient, - latestBlock$: result.network.latestBlock$, - storedBlockLogs$: result.network.storedBlockLogs$, - worldAddress: result.network.worldContract.address, - worldAbi: result.network.worldContract.abi, - write$: result.network.write$, - useStore: result.network.useStore, - }); - } }); diff --git a/examples/local-explorer/packages/client/src/mud/setupNetwork.ts b/examples/local-explorer/packages/client/src/mud/setupNetwork.ts index dba19e3f4c..2ff379e41e 100644 --- a/examples/local-explorer/packages/client/src/mud/setupNetwork.ts +++ b/examples/local-explorer/packages/client/src/mud/setupNetwork.ts @@ -16,9 +16,9 @@ import { import { syncToZustand } from "@latticexyz/store-sync/zustand"; import { getNetworkConfig } from "./getNetworkConfig"; import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; -import { createBurnerAccount, transportObserver, ContractWrite } from "@latticexyz/common"; -import { transactionQueue, writeObserver } from "@latticexyz/common/actions"; -import { Subject, share } from "rxjs"; +import { createBurnerAccount, transportObserver } from "@latticexyz/common"; +import { transactionQueue } from "@latticexyz/common/actions"; +import { observer, type WaitForStateChange } from "@latticexyz/explorer/observer"; /* * Import our MUD config, which includes strong types for @@ -34,6 +34,7 @@ export type SetupNetworkResult = Awaited>; export async function setupNetwork() { const networkConfig = await getNetworkConfig(); + const waitForStateChange = Promise.withResolvers(); /* * Create a viem public (read only) client @@ -47,12 +48,6 @@ export async function setupNetwork() { const publicClient = createPublicClient(clientOptions); - /* - * Create an observable for contract writes that we can - * pass into MUD dev tools for transaction observability. - */ - const write$ = new Subject(); - /* * Create a temporary wallet and a viem client for it * (see https://viem.sh/docs/clients/wallet.html). @@ -63,7 +58,11 @@ export async function setupNetwork() { account: burnerAccount, }) .extend(transactionQueue()) - .extend(writeObserver({ onWrite: (write) => write$.next(write) })); + .extend( + observer({ + waitForStateChange: (hash) => waitForStateChange.promise.then((fn) => fn(hash)), + }), + ); /* * Create an object for communicating with the deployed World. @@ -86,6 +85,7 @@ export async function setupNetwork() { publicClient, startBlock: BigInt(networkConfig.initialBlockNumber), }); + waitForStateChange.resolve(waitForTransaction); return { tables, @@ -96,6 +96,5 @@ export async function setupNetwork() { storedBlockLogs$, waitForTransaction, worldContract, - write$: write$.asObservable().pipe(share()), }; } diff --git a/examples/local-explorer/pnpm-lock.yaml b/examples/local-explorer/pnpm-lock.yaml index 35e2931c00..db7ba2d842 100644 --- a/examples/local-explorer/pnpm-lock.yaml +++ b/examples/local-explorer/pnpm-lock.yaml @@ -47,9 +47,9 @@ importers: '@latticexyz/common': specifier: link:../../../../packages/common version: link:../../../../packages/common - '@latticexyz/dev-tools': - specifier: link:../../../../packages/dev-tools - version: link:../../../../packages/dev-tools + '@latticexyz/explorer': + specifier: link:../../../../packages/explorer + version: link:../../../../packages/explorer '@latticexyz/react': specifier: link:../../../../packages/react version: link:../../../../packages/react diff --git a/package.json b/package.json index ff3e249430..425b16aca4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "build": "turbo run build", "changelog:generate": "tsx scripts/changelog.ts", "clean": "turbo run clean", - "dev": "TSUP_SKIP_DTS=true turbo run dev --concurrency 100 --filter=!@latticexyz/explorer", + "dev": "TSUP_SKIP_DTS=true turbo run dev --concurrency 100", "dist-tag-rm": "pnpm recursive exec -- sh -c 'npm dist-tag rm $(cat package.json | jq -r \".name\") $TAG || true'", "docs:generate:api": "tsx scripts/render-api-docs.ts", "foundryup": "curl -L https://foundry.paradigm.xyz | bash && bash ~/.foundry/bin/foundryup", diff --git a/packages/explorer/bin/explorer.js b/packages/explorer/bin/explorer.js new file mode 100755 index 0000000000..6d4f0839cf --- /dev/null +++ b/packages/explorer/bin/explorer.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +// workaround for https://github.com/pnpm/pnpm/issues/1801 +import "../dist/bin/explorer.js"; diff --git a/packages/explorer/package.json b/packages/explorer/package.json index 53f98600e5..806b76ee5d 100644 --- a/packages/explorer/package.json +++ b/packages/explorer/package.json @@ -3,23 +3,35 @@ "version": "2.2.3", "description": "World Explorer is a tool for visually exploring and manipulating the state of worlds", "type": "module", + "exports": { + "./observer": "./dist/exports/observer.js" + }, + "typesVersions": { + "*": { + "observer": [ + "./dist/exports/observer.d.ts" + ] + } + }, "bin": { - "explorer": "./dist/explorer.js" + "explorer": "./bin/explorer.js" }, "files": [ + "bin", "dist", ".next/standalone/packages/explorer" ], "scripts": { - "build": "pnpm run build:explorer && pnpm run build:bin", - "build:bin": "tsup", + "build": "pnpm run build:js && pnpm run build:explorer", "build:explorer": "next build && shx cp -r .next/static .next/standalone/packages/explorer/.next", - "clean": "pnpm run clean:explorer && pnpm run clean:bin", - "clean:bin": "shx rm -rf dist", + "build:js": "tsup", + "clean": "pnpm run clean:js && pnpm run clean:explorer", "clean:explorer": "shx rm -rf .next .turbo", - "dev": "next dev --port 13690", - "lint": "next lint", - "start": "node .next/standalone/packages/explorer/server.js" + "clean:js": "shx rm -rf dist", + "dev": "tsup --watch", + "explorer:dev": "next dev --port 13690", + "explorer:start": "node .next/standalone/packages/explorer/server.js", + "lint": "next lint" }, "dependencies": { "@hookform/resolvers": "^3.9.0", @@ -44,6 +56,7 @@ "better-sqlite3": "^8.6.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "debug": "^4.3.4", "lucide-react": "^0.408.0", "next": "14.2.5", "query-string": "^9.1.0", @@ -62,6 +75,7 @@ "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/better-sqlite3": "^7.6.4", + "@types/debug": "^4.1.7", "@types/minimist": "^1.2.5", "@types/node": "^18.15.11", "@types/react": "18.2.22", diff --git a/packages/explorer/src/app/(explorer)/error.tsx b/packages/explorer/src/app/(explorer)/error.tsx index 2c1604616b..4cfe8b5711 100644 --- a/packages/explorer/src/app/(explorer)/error.tsx +++ b/packages/explorer/src/app/(explorer)/error.tsx @@ -25,7 +25,7 @@ export default function Error({ reset, error }: Props) {
+ - {ACCOUNTS.map((address, index) => { - return ; + {connectors.map((connector) => { + return ; })} + + ); diff --git a/packages/explorer/src/components/ConnectButton.tsx b/packages/explorer/src/components/ConnectButton.tsx new file mode 100644 index 0000000000..5b544254f4 --- /dev/null +++ b/packages/explorer/src/components/ConnectButton.tsx @@ -0,0 +1,59 @@ +import { PlugIcon, ZapIcon } from "lucide-react"; +import { ConnectButton as RainbowConnectButton } from "@rainbow-me/rainbowkit"; +import { isAnvil } from "../common"; +import { cn } from "../lib/utils"; +import { AccountSelect } from "./AccountSelect"; +import { Button } from "./ui/Button"; + +export function ConnectButton() { + return ( + + {({ account, chain, openAccountModal, openChainModal, openConnectModal, mounted }) => { + const connected = mounted && account && chain; + + return ( +
+ {(() => { + if (!connected) { + if (isAnvil()) { + return ; + } + + return ( + + ); + } + + if (chain.unsupported) { + return ( + + ); + } + + return ( +
+ +
+ ); + })()} +
+ ); + }} +
+ ); +} diff --git a/packages/explorer/src/components/Navigation.tsx b/packages/explorer/src/components/Navigation.tsx index e4d0d7c2b3..5b883ae503 100644 --- a/packages/explorer/src/components/Navigation.tsx +++ b/packages/explorer/src/components/Navigation.tsx @@ -8,7 +8,7 @@ import { Separator } from "../components/ui/Separator"; import { useWorldUrl } from "../hooks/useWorldUrl"; import { cn } from "../lib/utils"; import { useAbiQuery } from "../queries/useAbiQuery"; -import { AccountSelect } from "./AccountSelect"; +import { ConnectButton } from "./ConnectButton"; export function Navigation() { const pathname = usePathname(); @@ -55,7 +55,7 @@ export function Navigation() {
- +
diff --git a/packages/explorer/src/components/ui/Button.tsx b/packages/explorer/src/components/ui/Button.tsx index 151aab1e65..e79f4a694e 100644 --- a/packages/explorer/src/components/ui/Button.tsx +++ b/packages/explorer/src/components/ui/Button.tsx @@ -39,9 +39,9 @@ export type ButtonProps = React.ButtonHTMLAttributes & VariantProps & { asChild?: boolean }; const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { + ({ className, variant, size, type = "button", asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button"; - return ; + return ; }, ); Button.displayName = "Button"; diff --git a/packages/explorer/src/connectors/anvil.ts b/packages/explorer/src/connectors/anvil.ts new file mode 100644 index 0000000000..9a3a8ad66a --- /dev/null +++ b/packages/explorer/src/connectors/anvil.ts @@ -0,0 +1,83 @@ +import { EIP1193RequestFn, Transport, WalletRpcSchema, http } from "viem"; +import { Account, privateKeyToAccount } from "viem/accounts"; +import { anvil as anvilChain } from "viem/chains"; +import { Connector, createConnector } from "wagmi"; +import { isAnvil } from "../common"; + +export const defaultAnvilAccounts = ( + [ + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", + "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6", + "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", + "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", + "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", + "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", + "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", + ] as const +).map((pk) => privateKeyToAccount(pk)); + +export type AnvilConnector = Connector & { + accounts: readonly Account[]; +}; + +export type AnvilConnectorOptions = { + id: string; + name: string; + accounts: readonly Account[]; +}; + +// We can't programmatically switch accounts within a connector, but we can switch between connectors, +// so create one anvil connector per default anvil account so users can switch between default anvil accounts. +export const defaultAnvilConnectors = defaultAnvilAccounts.map((account, i) => + anvil({ id: `anvil-${i}`, name: `Anvil #${i + 1}`, accounts: [account] }), +); + +export function isAnvilConnector(connector: Connector): connector is AnvilConnector { + return connector.type === "anvil"; +} + +export function anvil({ id, name, accounts }: AnvilConnectorOptions) { + if (!accounts.length) throw new Error("missing accounts"); + + type Provider = ReturnType>>; + + let connected = false; + return createConnector(() => ({ + id, + name, + type: "anvil", + accounts, + async connect() { + connected = true; + return { + accounts: accounts.map((a) => a.address), + chainId: anvilChain.id, + }; + }, + async disconnect() { + connected = false; + }, + async getAccounts() { + return accounts.map((a) => a.address); + }, + async getChainId() { + return anvilChain.id; + }, + async getProvider() { + return http()({ chain: anvilChain }); + }, + async isAuthorized() { + if (!isAnvil()) return false; + if (!connected) return false; + + const accounts = await this.getAccounts(); + return !!accounts.length; + }, + async onAccountsChanged() {}, + async onDisconnect() {}, + onChainChanged() {}, + })); +} diff --git a/packages/explorer/src/consts.ts b/packages/explorer/src/consts.ts deleted file mode 100644 index b6b5572fa3..0000000000 --- a/packages/explorer/src/consts.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Hex } from "viem"; -import { privateKeyToAccount } from "viem/accounts"; - -// private keys for local development testnet (anvil) -export const PRIVATE_KEYS: Hex[] = [ - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", - "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", - "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6", - "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", - "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", - "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", - "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", - "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", - "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", -]; - -export const ACCOUNTS: Hex[] = PRIVATE_KEYS.map((key) => privateKeyToAccount(key).address); - -export const ACCOUNT_PRIVATE_KEYS: Record = PRIVATE_KEYS.reduce( - (acc, key) => { - const account = privateKeyToAccount(key).address; - acc[account] = key; - return acc; - }, - {} as Record, -); diff --git a/packages/explorer/src/store/AppStoreProvider.tsx b/packages/explorer/src/store/AppStoreProvider.tsx deleted file mode 100644 index e3b35d8df8..0000000000 --- a/packages/explorer/src/store/AppStoreProvider.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client"; - -import { type ReactNode, createContext, useRef } from "react"; -import { createAppStore } from "./createAppStore"; - -export type AppStore = ReturnType; - -export const AppStoreContext = createContext(undefined); - -type Props = { - children: ReactNode; -}; - -export const AppStoreProvider = ({ children }: Props) => { - const storeRef = useRef(); - if (!storeRef.current) { - storeRef.current = createAppStore(); - } - - return {children}; -}; diff --git a/packages/explorer/src/store/createAppStore.ts b/packages/explorer/src/store/createAppStore.ts deleted file mode 100644 index 05f5e41991..0000000000 --- a/packages/explorer/src/store/createAppStore.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Hex } from "viem"; -import { createStore } from "zustand"; -import { ACCOUNTS } from "../consts"; - -export type AppStoreData = { - account: Hex; - setAccount: (account: Hex) => void; -}; - -export const createAppStore = () => { - return createStore()((set) => ({ - account: ACCOUNTS[0], - setAccount: (account) => set({ account }), - })); -}; diff --git a/packages/explorer/src/store/index.ts b/packages/explorer/src/store/index.ts deleted file mode 100644 index 6a67ea3b1e..0000000000 --- a/packages/explorer/src/store/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./createAppStore"; -export * from "./useAppStore"; -export * from "./AppStoreProvider"; diff --git a/packages/explorer/src/store/useAppStore.ts b/packages/explorer/src/store/useAppStore.ts deleted file mode 100644 index 0ff4c7a992..0000000000 --- a/packages/explorer/src/store/useAppStore.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useStore } from "zustand"; -import { useContext } from "react"; -import { AppStoreContext } from "./AppStoreProvider"; - -export const useAppStore = () => { - const appStoreContext = useContext(AppStoreContext); - - if (!appStoreContext) { - throw new Error(`useAppStore must be used within AppStoreProvider`); - } - - return useStore(appStoreContext); -}; From be0860fe35cfd68e30c14a04f945d9d64309e2c2 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Tue, 17 Sep 2024 00:39:41 -0500 Subject: [PATCH 20/20] docs(guide/replication): add a note that it's not something you're likely to need to do (#3174) Co-authored-by: ludens Co-authored-by: Kevin Ingersoll --- docs/pages/guides/_meta.js | 2 +- .../guides/replicating-onchain-state.mdx | 2 ++ docs/pages/introduction.mdx | 2 +- docs/pages/templates.mdx | 27 +++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 docs/pages/templates.mdx diff --git a/docs/pages/guides/_meta.js b/docs/pages/guides/_meta.js index 70fcd21bcc..5e98c55f70 100644 --- a/docs/pages/guides/_meta.js +++ b/docs/pages/guides/_meta.js @@ -1,9 +1,9 @@ export default { - "replicating-onchain-state": "Replicating onchain state", "hello-world": "Hello World", "extending-a-world": "Extending a World", "adding-delegation": "Adding Delegation", "modules": "Writing MUD Modules", emojimon: "Emojimon", testing: "Testing", + "replicating-onchain-state": "Replicating onchain state", }; diff --git a/docs/pages/guides/replicating-onchain-state.mdx b/docs/pages/guides/replicating-onchain-state.mdx index fa554d1fc1..7c890b14d6 100644 --- a/docs/pages/guides/replicating-onchain-state.mdx +++ b/docs/pages/guides/replicating-onchain-state.mdx @@ -2,6 +2,8 @@ This guide walks through how you might recreate onchain state from MUD's Store events. We use this pattern in our sync stack to hydrate a client or indexer from blockchain logs fetched from an RPC. +If you are using a MUD template, one of our sync packages, or indexers, all of this is already done for you. This guide is meant to demonstrate how the Store protocol works and show how you might implement a sync strategy or indexer of your own. + ## Store: MUD's onchain storage Before we get started, it's helpful to understand [how MUD stores its data on chain](/store/introduction). MUD introduces [a few new concepts](/store/data-model): tables, records, key tuples, and fields. diff --git a/docs/pages/introduction.mdx b/docs/pages/introduction.mdx index eea0810e2c..ec772564f1 100644 --- a/docs/pages/introduction.mdx +++ b/docs/pages/introduction.mdx @@ -8,7 +8,7 @@ MUD is a framework for ambitious onchain applications. It reduces the complexity Writing smart contracts is only a small part of building user-friendly EVM apps. Your frontend needs get data from the chain. You've kept your onchain logic simple to save users gas, but now getting data from the RPC is tricky and slow. So you spin up an indexer, write event handlers, and teach your frontend how to talk to another backend. Complicated, right? -MUD provides much of this out of the box. It uses a [familiar data model](/store/data-model) with tables and fields, built on a standardized storage protocol. This lets us provide you with an [automatic indexer](/services/indexer), no code is necessary to use it. And because it's all standardized, our client libraries already know how to get your app's onchain state and keep your frontend [in sync with the chain](/guides/replicating-onchain-state). +MUD provides much of this out of the box. It uses a [familiar data model](/store/data-model) with tables and fields, built on a standardized storage protocol. This lets us provide you with an [automatic indexer](/services/indexer), no code is necessary to use it. And because it's all standardized, our client libraries already know how to get your app's onchain state and keep your frontend in sync with the chain. MUD apps are [autonomous worlds](https://0xparc.org/blog/autonomous-worlds), infinitely extendable by default. They come with access control, upgradability, hooks, plugins, and a suite of great developer tools. diff --git a/docs/pages/templates.mdx b/docs/pages/templates.mdx new file mode 100644 index 0000000000..e1c24d83f7 --- /dev/null +++ b/docs/pages/templates.mdx @@ -0,0 +1,27 @@ +# Templates + +MUD templates include client code as well as an onchain MUD `World`. +These integrations include data synchronization, which gives the client a read-only copy of the data in the [MUD tables](/store/tables). + +## Official templates + +We wrote a number of integrations between TypeScript and MUD. + +- [Vanilla](/templates/typescript/vanilla) +- [React using RECS](/templates/typescript/react-ecs) +- React using Zustand +- [Three.js](/templates/typescript/threejs) + +## Community templates + +There are a number of other templates written by developers who wanted to use different frameworks with MUD. + +**Disclaimer:** We appreciate the people who created these integrations, but as we did not verify them ourselves, we cannot guarantee their functionality or security. + +- [Vue](/templates/typescript/vue) +- [Godot](/templates/godot) +- [Progressive Web App (mobile)](/templates/pwa) +- [Swift](/templates/swift) +- [Svelte](/templates/svelte) +- [Unity](/templates/unity) +- [Nethereum](/templates/nethereum)