diff --git a/.changeset/beige-ads-melt.md b/.changeset/beige-ads-melt.md new file mode 100644 index 0000000000..01846b4024 --- /dev/null +++ b/.changeset/beige-ads-melt.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": patch +--- + +Storage events are now emitted after "before" hooks, so that the resulting logs are now correctly ordered and reflect onchain logic. This resolves issues with store writes and event emissions happening in "before" hooks. diff --git a/.changeset/modern-brooms-rule.md b/.changeset/modern-brooms-rule.md new file mode 100644 index 0000000000..ecc2abe9f9 --- /dev/null +++ b/.changeset/modern-brooms-rule.md @@ -0,0 +1,25 @@ +--- +"@latticexyz/cli": patch +"@latticexyz/world-modules": patch +"@latticexyz/world": major +--- + +Previously `registerSystem` and `registerTable` had a side effect of registering namespaces if the system or table's namespace didn't exist yet. +This caused a possible frontrunning issue, where an attacker could detect a `registerSystem`/`registerTable` transaction in the mempool, +insert a `registerNamespace` transaction before it, grant themselves access to the namespace, transfer ownership of the namespace to the victim, +so that the `registerSystem`/`registerTable` transactions still went through successfully. +To mitigate this issue, the side effect of registering a namespace in `registerSystem` and `registerTable` has been removed. +Calls to these functions now expect the respective namespace to exist and the caller to own the namespace, otherwise they revert. + +Changes in consuming projects are only necessary if tables or systems are registered manually. +If only the MUD deployer is used to register tables and systems, no changes are necessary, as the MUD deployer has been updated accordingly. + +```diff ++ world.registerNamespace(namespaceId); + world.registerSystem(systemId, system, true); +``` + +```diff ++ world.registerNamespace(namespaceId); + MyTable.register(); +``` diff --git a/.changeset/pretty-toys-rescue.md b/.changeset/pretty-toys-rescue.md new file mode 100644 index 0000000000..45878e9dcc --- /dev/null +++ b/.changeset/pretty-toys-rescue.md @@ -0,0 +1,12 @@ +--- +"@latticexyz/store": patch +--- + +Aligned the order of function arguments in the `Storage` library. + +```solidity +store(uint256 storagePointer, uint256 offset, bytes memory data) +store(uint256 storagePointer, uint256 offset, uint256 length, uint256 memoryPointer) +load(uint256 storagePointer, uint256 offset, uint256 length) +load(uint256 storagePointer, uint256 offset, uint256 length, uint256 memoryPointer) +``` diff --git a/.changeset/seven-carpets-develop.md b/.changeset/seven-carpets-develop.md new file mode 100644 index 0000000000..c1ab18bf56 --- /dev/null +++ b/.changeset/seven-carpets-develop.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/world": patch +--- + +Namespace balances can no longer be transferred to non-existent namespaces. diff --git a/.changeset/young-poets-beam.md b/.changeset/young-poets-beam.md new file mode 100644 index 0000000000..0430659a3c --- /dev/null +++ b/.changeset/young-poets-beam.md @@ -0,0 +1,12 @@ +--- +"@latticexyz/store": minor +--- + +Improved error messages for invalid `FieldLayout`s + +```diff +-error FieldLayoutLib_InvalidLength(uint256 length); ++error FieldLayoutLib_TooManyFields(uint256 numFields, uint256 maxFields); ++error FieldLayoutLib_TooManyDynamicFields(uint256 numFields, uint256 maxFields); ++error FieldLayoutLib_Empty(); +``` diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d61aae167f..d7f7ee3a9b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,9 @@ on: - main pull_request: +env: + FOUNDRY_VERBOSITY: 5 + jobs: test: name: Run tests diff --git a/docs/pages/cli/config.mdx b/docs/pages/cli/config.mdx index 46ac1e6a9a..325d0c4c98 100644 --- a/docs/pages/cli/config.mdx +++ b/docs/pages/cli/config.mdx @@ -1 +1,126 @@ -# Config +# The World config + +Certain CLI commands, such as [`mud tablegen`](/cli/tablegen) and [`mud worldgen`](/cli/worldgen) require the MUD configuration file. +This file needs to be named `mud.config.ts` and be in the same folder as your `foundry.toml` file. + +The config is used to define: + +- The tables in your project in the `tables` object of your configuration. +- The [namespace](/world/namespaces-access-control) the [`System`s](/world/systems) and tables will be deployed in. +- The `System`s in your project. + By default, the deployer will find all Solidity matching `*System.sol` (so any file ending in `System.sol`, in any folder) and deploy them as public `System`. + If you want greater control over your systems (to change their public access or their name), you can use the `systems` object in the config. +- The [modules](/world/modules) that will be installed in the `World`. + +The is an example of a `World` config: + +```tsx +import { mudConfig } from "@latticexyz/world/register"; +import { resolveTableId } from "@latticexyz/config"; + +export default mudConfig({ + excludeSystems: ["System3", "System2"], + worldContractName: "CustomWorld", + namespace: "mud", + systems: { + IncrementSystem: { + name: "increment", + openAccess: true, + }, + }, + tables: { + CounterTable: { + valueSchema: { + value: "uint32", + }, + }, + }, + deploysDirectory: "./mud-deploys", + modules: [ + { + name: "KeysWithValueModule", + root: true, + args: [resolveTableId("CounterTable")], + }, + ], +}); +``` + +## Global configuration keys + +The global configuration keys are all optional. + +- **`namespace`**: a `string`: which namespace to deploy the resources defined in the config into. + The default value is the ROOT namespace. + +- **`excludeSystems`**: an array of `string`: which systems to not deploy, even if their name ends with “System”. + +- **`worldContractName`**: a `string`: the name of a contract in your project implementing the `IWorld` interface. + Useful if you want to modify the default World implementation, but potentially dangerous. +- **`deploysDirectory`** a `string`: which folder to put the deployment artifacts into after deployment. + +- **`modules`** an array of module definitions: each module definition has a `name`, `root` (optional), and `args` key. + + - `name`: Name of the module to install. The same module can be installed multiple times. This should be the name of the contract file without `.sol` (eg: if the file is named `DopeModule.sol`, the name of the module is `DopeModule`) + + - `root`: whether to create a `root` module or not. `root` modules have access to all tables and are not bound to namespace restrictions. + + - `args`: a list of arguments to be sent to the `install` function of the module. In this array, you can use the function `resolveTableId`. This function will turn a table name from your config into its low-level ID in the World. It is useful to pass references of a table to a module. + +- **`systems`**: a record of system definitions. The keys in the record are file names without the `.sol` extension. For example, if your system is named `TestSystem.sol`, use `TestSystem` as the key. + + The value is a record of system configuration properties: + + - `fileSelector` (optional): a `string`: the file selector for the system. + - `openAccess` (optional, default `true`): a `bool`: if set to `false`, only the systems in the same namespace and the addresses or systems listed in the the `accessList` array have access. + + - `accessList` (required if openAccess is `false`): an array of `string`. Each address in the array will be granted access to this system, allowing them to call it. + +- **`tables`**: a record of tables. The keys in the record are table names. + The value is a record of [table properties](https://github.com/latticexyz/mud/blob/main/packages/store/ts/config/storeConfig.ts#L110-L135). + + - **`valueSchema`** (record): + The keys of this record are the field names of the value (which should start with a lowercase letter). + The values are strings that contain the data types of the fields. + Note that this is the sole required field, all the others are optional. + + - **`keySchema`** (record): + The keys of this record are the field names of the key (which should start with a lowercase letter). + The values are strings that contain the data types of the fields. + + The default value is: + + ```json + { "key": "bytes32" } + ``` + + For a singleton table (one that contains a single row), use `{}` as the key schema. + + - **`directory`** (string): + Directory in which to create the table. + The default is `tables`, so by default tables are created in `src/codegen/tables`. + + - **`tableIdArgument`** (bool): + Make methods accept `tableId` argument instead of it being a hardcoded constant. + The default is `false` because you can achieve the same result using [`StoreSwitch`](/store/table-libraries#storeswitch). + + - **`storeArgument`** (bool): + Include methods that accept a manual `IStore` argument. + The default is `true`. + + - **`offchainOnly`**: (bool): + Table's information is available offchain (using [events](https://docs.soliditylang.org/en/latest/contracts.html#events)), but don't store in onchain. + These tables require a lot less gas. + + - **`dataStruct`**: (bool): + Include a [data struct](https://docs.soliditylang.org/en/v0.8.23/types.html#structs) and methods for it. + Default is false for 1-column tables; true for multi-column tables. + + Sample code for using a table library with `dataStruct`: + + ```solidity + // no data struct + MyTable.set(keccak256("some.key"), 1, 12, "foo"); + // data struct + MyTable.set(keccak256("some.key"), { field1: 1, field2: 12, stringField: "foo" }); + ``` diff --git a/docs/pages/cli/test.mdx b/docs/pages/cli/test.mdx index 3f17bf7649..1a6c4ff5bd 100644 --- a/docs/pages/cli/test.mdx +++ b/docs/pages/cli/test.mdx @@ -1,3 +1,115 @@ # mud test -**Coming soon** +This command runs the tests in a MUD project. Internally, it runs the following steps: + +1. Starts an [`anvil`](https://book.getfoundry.sh/reference/anvil/) instance. + {/* This could be either an empty blockchain, or one that can be forked for a [fork test](https://book.getfoundry.sh/forge/fork-testing). */} +1. Deploys the `World` and all related `System`s using [`mud deploy`](./deploy). +1. Runs tests using [`forge test`](https://book.getfoundry.sh/forge/tests) and passes the deployed world address to the tests via the `WORLD_ADDRESS` environment variable. + +## Command line options + +| Option | Meaning | Type | Default value | +| ----------------------- | ---------------------------------------------- | ------- | ---------------------------------------------------------- | +| `--version` | Show version number | boolean | `false` | +| `--configPath` | Path to the config file | string | `mud.config.ts` | +| `--printConfig` | Print the resolved config | boolean | `false` | +| `--saveDeployment` | Save the deployment info to a file | boolean | `true` | +| `--profile` | The foundry profile to use | string | `local` | +| `--srcDir` | Source directory | string | Foundry `src` directory | +| `--skipBuild` | Skip rebuilding the contracts before deploying | boolean | `false` | +| `--alwaysRunPostDeploy` | Run `PostDeploy.s.sol` after each deploy | boolean | `false` (run the script only when deploying a new `World`) | +| `--port` | Port for the testing `anvil` instance | number | 4242 | +| `--help` | Show help | boolean | `false` | + +## Examples + +```sh copy +pnpm mud test +``` + +## Writing MUD tests + +MUD test contracts inherit from [`MudTest`](https://github.com/latticexyz/mud/blob/main/packages/world/test/MudTest.t.sol). +This contract gets the `World` address from the `$WORLD_ADDRESS` environment variable and sets it as the `Store` address. + +
+ +Line by line explanation of a test + +This is an explanation of [the test](https://github.com/latticexyz/mud/blob/main/templates/react/packages/contracts/test/TasksTest.t.sol) for the [React template](https://github.com/latticexyz/mud/tree/main/templates/react) contracts. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import "forge-std/Test.sol"; +import { MudTest } from "@latticexyz/world/test/MudTest.t.sol"; +``` + +Import the general definitions required in all MUD tests. + +```solidity +import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { Tasks, TasksData } from "../src/codegen/index.sol"; +``` + +Import the definitions required for this test, the `World` we can access and the tables we'll use. + +```solidity +contract TasksTest is MudTest { + function testWorldExists() public { +``` + +MUD tests are [Foundry tests](https://book.getfoundry.sh/forge/tests). +Any public function that starts with `test` is a test that gets executed. + +```solidity + uint256 codeSize; + address addr = worldAddress; +``` + +The `World` address comes from the [`MudTest`](https://github.com/latticexyz/mud/blob/main/packages/world/test/MudTest.t.sol#L11). + +```solidity + assembly { + codeSize := extcodesize(addr) + } + assertTrue(codeSize > 0); + } +``` + +Use [`extcodesize`](https://www.evm.codes/#3b?fork=shanghai) to get the size of the `World` contract. +If the deploy process failed, there wouldn't be any code there. + +```solidity + function testTasks() public { + // Expect task to exist that we created during PostDeploy script + TasksData memory task = Tasks.get("1"); +``` + +Use the structure for a table entry's values that is created as part of code generation. + +```solidity + assertEq(task.description, "Walk the dog"); + assertEq(task.completedAt, 0); +``` + +Verify the information that is prepopulated by [the `PostDeploy.s.sol` script](https://github.com/latticexyz/mud/blob/main/templates/react/packages/contracts/script/PostDeploy.s.sol). + +```solidity + // Expect the task to be completed after calling completeTask from our TasksSystem + IWorld(worldAddress).completeTask("1"); +``` + +Call a `System` to modify the table data. + +```solidity + assertEq(Tasks.getCompletedAt("1"), block.timestamp); + } +} +``` + +Verify that the call changed the data correctly. + +
diff --git a/docs/pages/contribute.mdx b/docs/pages/contribute.mdx index 002d942b9f..c8747ff53d 100644 --- a/docs/pages/contribute.mdx +++ b/docs/pages/contribute.mdx @@ -1,11 +1,11 @@ -## Contribute +# Contribute We'd love your support in improving MUD! [The MUD monorepo](https://github.com/latticexyz/mud) includes all of MUD's source code, and pull requests are always welcome. To discuss new features or changes [join our Discord](https://lattice.xyz/discord). You can find a list of good first issues (that don't require a lot of context on the full system) at [contribute.mud.dev](https://contribute.mud.dev). -### Local development setup +## Local development setup The following steps are only necessary if you want to contribute to MUD. To use MUD in your project, [set up a new project with the MUD CLI](/quick-start). @@ -32,7 +32,18 @@ The following steps are only necessary if you want to contribute to MUD. To use pnpm build ``` -### Pull requests +### Using your repository in an application + +To check the changes in an application, run these commands at the application's root. + +```sh copy +pnpm mud set-version --link +pnpm install +``` + +While the application's `pnpm dev` is running, any time you `pnpm build` the MUD repository the application gets updated automatically. + +## Pull requests MUD follows the [conventional commit specification](https://www.conventionalcommits.org/en/v1.0.0/) for PR titles. Please keep the scope of your PR small (rather open multiple small PRs than one huge PR). diff --git a/docs/pages/guides/extend-ui-with-msgs.png b/docs/pages/guides/extend-ui-with-msgs.png new file mode 100644 index 0000000000..0553f613bb Binary files /dev/null and b/docs/pages/guides/extend-ui-with-msgs.png differ diff --git a/docs/pages/guides/extending-world.mdx b/docs/pages/guides/extending-world.mdx index 6309d21ce8..31748bda84 100644 --- a/docs/pages/guides/extending-world.mdx +++ b/docs/pages/guides/extending-world.mdx @@ -1,3 +1,5 @@ +import { CollapseCode } from "../../components/CollapseCode"; + # Extending World On this page you learn how to modify a `World` that is already deployed to the blockchain. @@ -6,7 +8,7 @@ If you want to learn how to modify a `World` before it is deployed, [see the hel ## The sample program To learn how to extend a `World`, we will extend the `Counter` example to allow users to leave a message while incrementing the counter. -The steps to create the example `World` that we'll extend [are here](/templates/typescript/getting-started). +[Follow these steps to create the example `World` that we'll extend](/templates/typescript/getting-started). You can either do it now, or wait until after you've created the extension resources. To extend the `World` with the message functionality requires adding several resources: @@ -26,7 +28,7 @@ To extend the `World` with the message functionality requires adding several res The easiest way to create the Solidity code is to use the MUD template: 1. Create a new MUD application. - Use either the `vanilla` template or the `react-ecs` one. + Use the `vanilla` template. ```sh copy pnpm create mud@next extension @@ -35,12 +37,16 @@ The easiest way to create the Solidity code is to use the MUD template: 1. Edit `mud.config.ts` to include the definitions we need. - ```ts filename="mud.config.ts" copy + ```ts filename="mud.config.ts" copy showLineNumbers {4,10-17,19-24} import { mudConfig } from "@latticexyz/world/register"; export default mudConfig({ namespace: "messaging", tables: { + Counter: { + keySchema: {}, + valueSchema: "uint32", + }, Messages: { keySchema: { counterValue: "uint32", @@ -146,8 +152,8 @@ The easiest way to create the Solidity code is to use the MUD template: ## Deploy to the blockchain -1. To have a `Counter` example `World` to modify, go to a separate command line window and create a blockchain with a `World` using [the TypeScript template](/template/typescript/getting-started) and start the execution. - Choose either the **react** user interface or the **vanilla** one. +1. To have a `World` with the `Counter` example to modify, go to a separate command line window and create a blockchain with a `World` using [the TypeScript template](/template/typescript/getting-started) and start the execution. + Choose either the `react-ecs` user interface or the `vanilla` one. ```sh copy pnpm create mud@next extendMe @@ -203,7 +209,7 @@ The easiest way to create the Solidity code is to use the MUD template: WorldRegistrationSystem world = WorldRegistrationSystem(worldAddress); ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("messaging")); - ResourceId systemResource = WorldResourceIdLib.encode(RESOURCE_SYSTEM, "messaging", "message"); + ResourceId systemResource = WorldResourceIdLib.encode(RESOURCE_SYSTEM, "messaging", "MessageSystem"); vm.startBroadcast(deployerPrivateKey); world.registerNamespace(namespaceResource); @@ -295,9 +301,11 @@ The easiest way to create the Solidity code is to use the MUD template: ```solidity WorldRegistrationSystem world = WorldRegistrationSystem(worldAddress); ResourceId namespaceResource = WorldResourceIdLib.encodeNamespace(bytes14("messaging")); - ResourceId systemResource = WorldResourceIdLib.encode(RESOURCE_SYSTEM, "messaging", "message"); + ResourceId systemResource = WorldResourceIdLib.encode(RESOURCE_SYSTEM, "messaging", "MessageSystem"); ``` + We don't _have_ to name the system the same as the contract name, but it makes writing the client code later easier. + Among other things, a MUD `World` is a `WorldRegistrationSystem`, so it has the appropriate functions. A `ResourceId` is a 32 byte value that uniquely identifies a resource in a MUD `World`. It is two bytes of resource type followed by 14 bytes of namespace and then 16 bytes of the name of the actual resource. @@ -307,7 +315,7 @@ The easiest way to create the Solidity code is to use the MUD template: | Name | Type | Namespace | Resource name | | ----------------- | ---------------- | --------- | ------------- | | namespaceResource | `ns` (namespace) | messaging | Empty | - | systemResource | `sy` (system) | messaging | message | + | systemResource | `sy` (system) | messaging | MessageSystem | If you want to see these values, add these two lines to the script: @@ -324,15 +332,15 @@ The easiest way to create the Solidity code is to use the MUD template: | Name | Expected value | | ------------ | ------------------------------------------------------------------ | | Namespace ID | 0x6e736d6573736167696e67000000000000000000000000000000000000000000 | - | System ID | 0x73796d6573736167696e6700000000006d657373616765000000000000000000 | + | System ID | 0x73796d6573736167696e6700000000004d65737361676553797374656d000000 | You can use [an online calculator](https://www.duplichecker.com/hex-to-text.php) to verify the values are correct. - | Hex value | ASCII | - | ---------------------: | ----------- | - | 6e736d6573736167696e67 | nsmessaging | - | 73796d6573736167696e67 | symessaging | - | 6d657373616765 | message | + | Hex value | ASCII | + | -------------------------: | ------------- | + | 6e736d6573736167696e67 | nsmessaging | + | 73796d6573736167696e67 | symessaging | + | 4d65737361676553797374656d | MessageSystem | ```solidity vm.startBroadcast(deployerPrivateKey); @@ -393,7 +401,7 @@ The easiest way to create the Solidity code is to use the MUD template: ```sh copy source .env - cast send $WORLD_ADDRESS --private-key $PRIVATE_KEY "messaging_message_incrementMessage(string)" "hello" + cast send $WORLD_ADDRESS --rpc-url http://localhost:8545 --private-key $PRIVATE_KEY "messaging_MessageSystem_incrementMessage(string)" "hello" ``` When a function is _not_ in the root namespace, it is accessible as `__` (as long as it is [registered](https://github.com/latticexyz/mud/blob/main/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol#L164-L201)). @@ -406,7 +414,7 @@ The easiest way to create the Solidity code is to use the MUD template: ```sh copy TABLE_ID=0x74626d6573736167696e6700000000004d657373616765730000000000000000 KEY=[`cast to-int256 2`] - RAW_RESULT=`cast call $WORLD_ADDRESS "getRecord(bytes32,bytes32[])" $TABLE_ID $KEY` + RAW_RESULT=`cast call $WORLD_ADDRESS --rpc-url http://localhost:8545 "getRecord(bytes32,bytes32[])" $TABLE_ID $KEY` cast --to-ascii ${RAW_RESULT:322:-2} ``` @@ -463,10 +471,10 @@ The easiest way to create the Solidity code is to use the MUD template: | ----: | ---------------: | --------------------------------------------------------------------------- | | 6-0 | `00000000000005` | The total length of all dynamic fields is five bytes. | | 11-7 | `0000000005` | The length of the first dynamic field is five bytes. | - | 16-12 | `0000000000` | The length of the (non existent in `Messages`) second dynamic field is zero | - | 21-17 | `0000000000` | The length of the (non existent in `Messages`) third dynamic field is zero | - | 26-22 | `0000000000` | The length of the (non existent in `Messages`) fourth dynamic field is zero | - | 31-27 | `0000000000` | The length of the (non existent in `Messages`) fifth dynamic field is zero | + | 16-12 | `0000000000` | The length of the (non-existent in `Messages`) second dynamic field is zero | + | 21-17 | `0000000000` | The length of the (non-existent in `Messages`) third dynamic field is zero | + | 26-22 | `0000000000` | The length of the (non-existent in `Messages`) fourth dynamic field is zero | + | 31-27 | `0000000000` | The length of the (non-existent in `Messages`) fifth dynamic field is zero | MUD tables can only have up to five dynamic fields because `PackedCounter` needs to fit in a 32 byte word. @@ -484,3 +492,312 @@ The easiest way to create the Solidity code is to use the MUD template: The next 320 characters are words 0-4, which are not the actual message (each word is 32 bytes, which is 64 hexadecimal digits). We also don't need the trailing newline. `${RAW_RESULT:322:-2}` removes those characters so we can use [`cast`](https://book.getfoundry.sh/reference/cast/cast-to-ascii) to get the ASCII. + +## Create an updated user interface + +Finally, we create a user interface that supports the messaging system. + +### Run the user interface + +For the user interface to run properly, it cannot require resources that the template user interface is already using, or that are not available because of the changes we made in `packages/contracts`. +Here we modify the template UI to so it will run properly. + +1. Change to the client package of the extension. + + ```sh copy + cd ../client + ``` + +1. Port 3000 is already in use by the `extendMe` application, so edit `vite.config.ts` to use a different port. + + + + ```ts filename="vite.config.ts" copy showLineNumbers {5} + import { defineConfig } from "vite"; + + export default defineConfig({ + server: { + port: 3001, + fs: { + strict: false, + }, + }, + build: { + target: "es2022", + minify: true, + sourcemap: true, + }, + }); + ``` + + + +1. Start the user interface. + + ```sh copy + pnpm vite + ``` + +1. Open the application at [http://localhost:3001](http://localhost:3001). + The client will throw errors because we didn't update the client code to work with the new contracts yet - we'll fix that in the next steps. + +1. The network configuration does not have the correct `World` address. + Get the `World` address from the application being extended. + By default, you can see this value on the application at URL [http://localhost:3000](http://localhost:3000). + Edit `../contracts/worlds.json` to set the `World` address for the chain ID (`31337`). + + Note: If you followed the directions above to create `extendMe`, the `World` address is `0x6e9474e9c83676b9a71133ff96db43e7aa0a4342`. + + ```json copy {3} + { + "31337": { + "address": "0x6e9474e9c83676b9a71133ff96db43e7aa0a4342" + } + } + ``` + +Note that while at this point you can access the user interface, the counter value is inaccurate and if you click **Increment** you get an error. +We will fix these problems now. + +### Increment and send a message + +Edit `src/mud/createSystemCalls.ts` to use the new `messaging_MessageSystem_incrementMessage` function instead of `increment`. + + + + ```ts filename="createSystemCalls.ts" copy showLineNumbers {43-44} + /* + * Create the system calls that the client can use to ask + * for changes in the World state (using the System contracts). + */ + + import { getComponentValue } from "@latticexyz/recs"; + import { ClientComponents } from "./createClientComponents"; + import { SetupNetworkResult } from "./setupNetwork"; + import { singletonEntity } from "@latticexyz/store-sync/recs"; + + export type SystemCalls = ReturnType; + + export function createSystemCalls( + /* + * The parameter list informs TypeScript that: + * + * - The first parameter is expected to be a + * SetupNetworkResult, as defined in setupNetwork.ts + * + * Out of this parameter, we only care about two fields: + * - worldContract (which comes from getContract, see + * https://github.com/latticexyz/mud/blob/main/templates/vanilla/packages/client/src/mud/setupNetwork.ts#L63-L69). + * + * - waitForTransaction (which comes from syncToRecs, see + * https://github.com/latticexyz/mud/blob/main/templates/vanilla/packages/client/src/mud/setupNetwork.ts#L77-L83). + * + * - From the second parameter, which is a ClientComponent, + * we only care about Counter. This parameter comes to use + * through createClientComponents.ts, but it originates in + * syncToRecs + * (https://github.com/latticexyz/mud/blob/main/templates/vanilla/packages/client/src/mud/setupNetwork.ts#L77-L83). + */ + { worldContract, waitForTransaction }: SetupNetworkResult, + { Counter }: ClientComponents + ) { + const increment = async () => { + /* + * Because IncrementSystem + * (https://mud.dev/templates/typescript/contracts#incrementsystemsol) + * is in the root namespace, `.increment` can be called directly + * on the World contract. + */ + const tx = await worldContract.write.messaging_MessageSystem_incrementMessage( + [`message at ${Date()}`]); + await waitForTransaction(tx); + return getComponentValue(Counter, singletonEntity); + }; + + return { + increment, + }; + } + ``` + + + +Now you can click **Increment** in the user interface and create messages. +The user interface does not display those messages, but you can open the **MUD Dev Tools**, click the **Components** tab, and select **messagings:Messages** to view them. + +![The extended UI with the messages shown in the MUD Dev Tools](./extend-ui-with-msgs.png) + +### Read the counter + +We still don't have the `Counter` value available, because the definition in `mud.config.ts` is in the `messaging` namespace, not the root one. +We will now fix this. + +1. Add the `@latticexyz/store` package. + + ```ts sh copy + pnpm add @latticexyz/store@next + ``` + +1. Copy the `mud.config.ts` from the application being extended (`extendMe`) to `src/mud/counter.config.ts`. + + ```ts filename="counter.config.ts" copy + import { mudConfig } from "@latticexyz/world/register"; + + export default mudConfig({ + tables: { + Counter: { + keySchema: {}, + valueSchema: "uint32", + }, + }, + }); + ``` + +1. Edit `src/mud/setupNetwork.ts` to add a `tables` parameter with the `Counter` object to the `syncToRecs` call. + + + + ```ts filename="setupNetwork.ts" copy showLineNumbers {16,27,82} + /* + * The MUD client code is built on top of viem + * (https://viem.sh/docs/getting-started.html). + * This line imports the functions we need from it. + */ + import { + createPublicClient, + fallback, + webSocket, + http, + createWalletClient, + Hex, + parseEther, + ClientConfig, + } from "viem"; + import { createFaucetService } from "@latticexyz/services/faucet"; + import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs"; + + import { getNetworkConfig } from "./getNetworkConfig"; + import { world } from "./world"; + import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; + import { createBurnerAccount, getContract, transportObserver, ContractWrite } from "@latticexyz/common"; + + import { Subject, share } from "rxjs"; + import { resolveConfig } from "@latticexyz/store"; + + /* + * Import our MUD config, which includes strong types for + * our tables and other config options. We use this to generate + * things like RECS components and get back strong types for them. + * + * See https://mud.dev/templates/typescript/contracts#mudconfigts + * for the source of this information. + */ + import mudConfig from "contracts/mud.config"; + import counterConfig from "./counter.config"; + + export type SetupNetworkResult = Awaited>; + + export async function setupNetwork() { + const networkConfig = await getNetworkConfig(); + + /* + * Create a viem public (read only) client + * (https://viem.sh/docs/clients/public.html) + */ + const clientOptions = { + chain: networkConfig.chain, + transport: transportObserver(fallback([webSocket(), http()])), + pollingInterval: 1000, + } as const satisfies ClientConfig; + + const publicClient = createPublicClient(clientOptions); + + /* + * Create a temporary wallet and a viem client for it + * (see https://viem.sh/docs/clients/wallet.html). + */ + const burnerAccount = createBurnerAccount(networkConfig.privateKey as Hex); + const burnerWalletClient = createWalletClient({ + ...clientOptions, + account: burnerAccount, + }); + + /* + * Create an observable for contract writes that we can + * pass into MUD dev tools for transaction observability. + */ + const write$ = new Subject(); + + /* + * Create an object for communicating with the deployed World. + */ + const worldContract = getContract({ + address: networkConfig.worldAddress as Hex, + abi: IWorldAbi, + publicClient, + walletClient: burnerWalletClient, + onWrite: (write) => write$.next(write), + }); + + /* + * Sync on-chain state into RECS and keeps our client in sync. + * Uses the MUD indexer if available, otherwise falls back + * to the viem publicClient to make RPC calls to fetch MUD + * events from the chain. + */ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ + world, + config: mudConfig, + tables: resolveConfig(counterConfig).tables, + address: networkConfig.worldAddress as Hex, + publicClient, + startBlock: BigInt(networkConfig.initialBlockNumber), + }); + + /* + * If there is a faucet, request (test) ETH if you have + * less than 1 ETH. Repeat every 20 seconds to ensure you don't + * run out. + */ + if (networkConfig.faucetServiceUrl) { + const address = burnerAccount.address; + console.info("[Dev Faucet]: Player address -> ", address); + + const faucet = createFaucetService(networkConfig.faucetServiceUrl); + + const requestDrip = async () => { + const balance = await publicClient.getBalance({ address }); + console.info(`[Dev Faucet]: Player balance -> ${balance}`); + const lowBalance = balance < parseEther("1"); + if (lowBalance) { + console.info("[Dev Faucet]: Balance is low, dripping funds to player"); + // Double drip + await faucet.dripDev({ address }); + await faucet.dripDev({ address }); + } + }; + + requestDrip(); + // Request a drip every 20 seconds + setInterval(requestDrip, 20000); + } + + return { + world, + components, + playerEntity: encodeEntity({ address: "address" }, { address: burnerWalletClient.account.address }), + publicClient, + walletClient: burnerWalletClient, + latestBlock$, + storedBlockLogs$, + waitForTransaction, + worldContract, + write$: write$.asObservable().pipe(share()), + }; + } + ``` + + + +Now you can click **Increment** and see the update. +Also, you can look in **Components > :Counter** and see the counter value. diff --git a/docs/pages/guides/hello-world.mdx b/docs/pages/guides/hello-world.mdx index 71448d751f..41eb498950 100644 --- a/docs/pages/guides/hello-world.mdx +++ b/docs/pages/guides/hello-world.mdx @@ -3,10 +3,12 @@ On this page you learn how to modify a `World` before it is deployed to the blockchain. If you want to learn how to modify a `World` that is already deployed, [see the extending world page](./extending-world). -The examples on this page use [the vanilla template](../templates/typescript/vanilla). -[See this page for installation instructions](../templates/typescript/getting-started). -Every example is written under the assumption that it is independent, and built on top of a new vanilla template. +The examples on this page use [the vanilla template](../template/typescript/vanilla). +[See this page for installation instructions](../template/typescript/getting-started). +Most examples are written under the assumption that they are independent, and built on top of a new vanilla template. +The exception is filtering data synchronization, because it needs a table that isn't a singleton. - [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). diff --git a/docs/pages/guides/hello-world/_meta.js b/docs/pages/guides/hello-world/_meta.js index 45d104aca8..a9f17b5005 100644 --- a/docs/pages/guides/hello-world/_meta.js +++ b/docs/pages/guides/hello-world/_meta.js @@ -1,5 +1,6 @@ export default { "add-table": "Add a table", + "filter-sync": "Filter data synchronization", "add-system": "Add a system", "deploy": { "title": "Deploy to a blockchain", diff --git a/docs/pages/guides/hello-world/filter-sync.mdx b/docs/pages/guides/hello-world/filter-sync.mdx new file mode 100644 index 0000000000..d96eef92b7 --- /dev/null +++ b/docs/pages/guides/hello-world/filter-sync.mdx @@ -0,0 +1,229 @@ +import { CollapseCode } from "../../../components/CollapseCode"; + +# Filter data synchronization + +In this tutorial you modify `networkSetup.ts` to filter the information you synchronize. +Filtering information this way allows you to reduce the use of network resources and makes loading times faster. + +## Setup + +To see the effects of filtering we need a table with entries to filter. To get such a table: + +1. [Create a new MUD application from the `vanilla` template](../../templates/typescript/getting-started). +1. [Run the guide to add a table](./add-table). + +## Filtering + +Edit `packages/client/src/mud/setupNetwork.ts`. + +- Import [`pad`](https://viem.sh/docs/utilities/pad.html) from viem. +- Import [`resourceToHex`](https://github.com/latticexyz/mud/blob/main/packages/common/src/resourceToHex.ts) to create resource identifiers. +- Add a `filters` field to the `syncToRecs` call. + + + +```ts filename="setupNetwork.ts" copy showLineNumbers {14-15,85-97} +/* + * The MUD client code is built on top of viem + * (https://viem.sh/docs/getting-started.html). + * This line imports the functions we need from it. + */ +import { createPublicClient, fallback, webSocket, http, createWalletClient, Hex, parseEther, ClientConfig } from "viem"; +import { createFaucetService } from "@latticexyz/services/faucet"; +import { encodeEntity, syncToRecs } from "@latticexyz/store-sync/recs"; + +import { getNetworkConfig } from "./getNetworkConfig"; +import { world } from "./world"; +import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; +import { createBurnerAccount, getContract, transportObserver, ContractWrite } from "@latticexyz/common"; +import { resourceToHex } from "@latticexyz/common"; +import { pad } from "viem"; + +import { Subject, share } from "rxjs"; + +/* + * Import our MUD config, which includes strong types for + * our tables and other config options. We use this to generate + * things like RECS components and get back strong types for them. + * + * See https://mud.dev/templates/typescript/contracts#mudconfigts + * for the source of this information. + */ +import mudConfig from "contracts/mud.config"; + +export type SetupNetworkResult = Awaited>; + +export async function setupNetwork() { + const networkConfig = await getNetworkConfig(); + + /* + * Create a viem public (read only) client + * (https://viem.sh/docs/clients/public.html) + */ + const clientOptions = { + chain: networkConfig.chain, + transport: transportObserver(fallback([webSocket(), http()])), + pollingInterval: 1000, + } as const satisfies ClientConfig; + + const publicClient = createPublicClient(clientOptions); + + /* + * Create a temporary wallet and a viem client for it + * (see https://viem.sh/docs/clients/wallet.html). + */ + const burnerAccount = createBurnerAccount(networkConfig.privateKey as Hex); + const burnerWalletClient = createWalletClient({ + ...clientOptions, + account: burnerAccount, + }); + + /* + * Create an observable for contract writes that we can + * pass into MUD dev tools for transaction observability. + */ + const write$ = new Subject(); + + /* + * Create an object for communicating with the deployed World. + */ + const worldContract = getContract({ + address: networkConfig.worldAddress as Hex, + abi: IWorldAbi, + publicClient, + walletClient: burnerWalletClient, + onWrite: (write) => write$.next(write), + }); + + /* + * Sync on-chain state into RECS and keeps our client in sync. + * Uses the MUD indexer if available, otherwise falls back + * to the viem publicClient to make RPC calls to fetch MUD + * events from the chain. + */ + const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ + world, + config: mudConfig, + address: networkConfig.worldAddress as Hex, + publicClient, + startBlock: BigInt(networkConfig.initialBlockNumber), + filters: [ + { + tableId: resourceToHex({ type: "table", namespace: "", name: "Counter" }), + }, + { + tableId: resourceToHex({ type: "table", namespace: "", name: "History" }), + key0: pad("0x01"), + }, + { + tableId: resourceToHex({ type: "table", namespace: "", name: "History" }), + key0: pad("0x05"), + }, + ], + }); + + /* + * If there is a faucet, request (test) ETH if you have + * less than 1 ETH. Repeat every 20 seconds to ensure you don't + * run out. + */ + if (networkConfig.faucetServiceUrl) { + const address = burnerAccount.address; + console.info("[Dev Faucet]: Player address -> ", address); + + const faucet = createFaucetService(networkConfig.faucetServiceUrl); + + const requestDrip = async () => { + const balance = await publicClient.getBalance({ address }); + console.info(`[Dev Faucet]: Player balance -> ${balance}`); + const lowBalance = balance < parseEther("1"); + if (lowBalance) { + console.info("[Dev Faucet]: Balance is low, dripping funds to player"); + // Double drip + await faucet.dripDev({ address }); + await faucet.dripDev({ address }); + } + }; + + requestDrip(); + // Request a drip every 20 seconds + setInterval(requestDrip, 20000); + } + + return { + world, + components, + playerEntity: encodeEntity({ address: "address" }, { address: burnerWalletClient.account.address }), + publicClient, + walletClient: burnerWalletClient, + latestBlock$, + storedBlockLogs$, + waitForTransaction, + worldContract, + write$: write$.asObservable().pipe(share()), + }; +} +``` + + + +Click **Increment** a few times to see you only see the history for counter values 1 and 5. +You can also go to the MUD Dev Tools and see that when you select **Components > :History** it only has those lines. + +### Explanation + +The `filters` field contains a list of filters. +Only rows that match at least one line are synchronized. +Each filter is a structure that can have up to three fields, and all the fields that are specified must match a row for the filter to match. + +- `tableId`, the table ID to synchronize. + You create this value using [`resourceToHex`](https://github.com/latticexyz/mud/blob/main/packages/common/src/resourceToHex.ts), the type can be either `table`, or `offchainTable`. +- `key0`, the first key value (as a 32 byte hexadecimal string). +- `key1`, the second key value (as a 32 byte hexadecimal string). + +
+ +The filters in the code sample + +```ts + filters: [ + { + tableId: resourceToHex({ type: "table", namespace: "", name: "Counter"}), + }, +``` + +The first filter is for the `:Counter` table (`Counter` in the root namespace). +We don't specify any keys, because we want all the rows of the table. +It's a singleton so there is only one row anyway. + +```ts + { + tableId: resourceToHex({ type: "table", namespace: "", name: "History"}), + key0: pad("0x01"), + }, + { + tableId: resourceToHex({ type: "table", namespace: "", name: "History"}), + key0: pad("0x05"), + }, + ], +``` + +These two filters apply to the `:History` table. +This table has just one key, the counter value which the row documents. +We need a separate filter for every value, and here we have two we care about: `1` and `5`. + +
+ +## Limitations + +There are several limitations on filters. + +- We can only filter on these fields: + - The table ID (`tableId`) + - The first key (`key0`) + - The second key (`key1`) +- We can only filter by checking for equality. + We cannot check ranges, or get all values except for a specific one (inequality). + +Of course, once we have the data we can filter it any way we want. +The purpose of these filters is to restrict the information we get at all, either directly from the blockchain or from the indexer. diff --git a/docs/pages/services/indexer.mdx b/docs/pages/services/indexer.mdx index 6fcb437f9f..1b3831be45 100644 --- a/docs/pages/services/indexer.mdx +++ b/docs/pages/services/indexer.mdx @@ -1,3 +1,5 @@ +import { CollapseCode } from "../../components/CollapseCode"; + # Indexer The MUD Indexer is an offchain indexer for onchain applications built with MUD. @@ -180,7 +182,7 @@ The easiest way to test the indexer is to [run the template as a world](/templat #### SQLite The command to start the indexer in SQLite mode is `pnpm start:sqlite`. -To index an `anvil` instance running to the host, use: +To index an `anvil` instance running to the host you can use this command. ```sh copy docker run \ @@ -336,6 +338,28 @@ Meaning that query 0 is for everything in the `World` at address `0x6e9474e9c836 +## Using the indexer + +The source code of a MUD client has a call either to [`syncToRecs`](https://github.com/latticexyz/mud/blob/main/packages/store-sync/src/recs/syncToRecs.ts#L21-L82) or to [`syncToZustand`](https://github.com/latticexyz/mud/blob/main/packages/store-sync/src/zustand/syncToZustand.ts#L33-L74), typically in [`setupNetwork.ts`](https://github.com/latticexyz/mud/blob/main/templates/react/packages/client/src/mud/setupNetwork.ts#L74-L79). +This call initializes the synchronization between the data source (RPC or indexer) and the client's copy. + +To use the indexer, specify an `indexerUrl` parameter with the URL. + + + +```ts {7} +const { components, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToRecs({ + world, + config: mudConfig, + address: networkConfig.worldAddress as Hex, + publicClient, + startBlock: BigInt(networkConfig.initialBlockNumber), + indexerUrl: "http://127.0.0.1:3001", +}); +``` + + + ## Usage examples ### SQLite to view the tasks from the React template diff --git a/docs/pages/state-query/typescript/zustand.mdx b/docs/pages/state-query/typescript/zustand.mdx index 50a4e4af89..f27bbcf564 100644 --- a/docs/pages/state-query/typescript/zustand.mdx +++ b/docs/pages/state-query/typescript/zustand.mdx @@ -457,8 +457,7 @@ To read data you use `useStore.getState()`. It supports the same functions as th 1. Browse to the application. -1. Open the console. - [Here are in the instructions on how to do it in Chrome](https://developer.chrome.com/docs/devtools/console/javascript/). +1. [Open the console (see Chrome instructions)](https://developer.chrome.com/docs/devtools/console/javascript/). 1. Run this command to call `zustand` which we defined in `index.ts`: diff --git a/docs/pages/store/encoding.mdx b/docs/pages/store/encoding.mdx index 2b300f4e00..1f2119621e 100644 --- a/docs/pages/store/encoding.mdx +++ b/docs/pages/store/encoding.mdx @@ -325,17 +325,17 @@ It is used as an onchain gas optimization for storage operations on static lengt Example: `FieldLayout` with 3 fields (`uint64`, `uint40`, `address[]`). ``` -0x 0010 02 01 08080000000000000000000000000000000000000000000000000000 +0x 000D 02 01 08050000000000000000000000000000000000000000000000000000 |----|--|--|--------------------------------------------------------| | | | | Up to 28 static byte lengths, one byte each | - | | | | 8 (uint64), 5 (uint40), 0 (address[]) | + | | | | 8 (uint64), 5 (uint40) | |----|--|--|--------------------------------------------------------| | | | Number of dynamic length fields | | | | 1 | - |----|--|--|--------------------------------------------------------| + |----|--|-----------------------------------------------------------| | | Number of static length fields | | | 2 | - |----|--|--|--------------------------------------------------------| + |----|--------------------------------------------------------------| | Total byte length of all static length fields | | 13 | ``` diff --git a/docs/pages/templates/godot.mdx b/docs/pages/templates/godot.mdx index ea5056e8a4..af34f2a13e 100644 --- a/docs/pages/templates/godot.mdx +++ b/docs/pages/templates/godot.mdx @@ -2,8 +2,8 @@ import Disclaimer from "./disclaimer.mdx"; # Godot -[Godot](https://docs.godotengine.org/en/stable/) is a free open source game engine. -[The integration is here](https://github.com/Digital-Avatars-and-Robotics/MUDxGodot). -If you have any question, you can [open an issue in the repository](https://github.com/Digital-Avatars-and-Robotics/MUDxGodot/issues). +[Godot](https://docs.godotengine.org/en/stable/) is a free open-source game engine. +[You can see the source code here](https://github.com/Digital-Avatars-and-Robotics/MUDxGodot). +If you have any questions, you can [open an issue in the repository](https://github.com/Digital-Avatars-and-Robotics/MUDxGodot/issues). diff --git a/docs/pages/templates/swift.mdx b/docs/pages/templates/swift.mdx index f235e5e01a..ce01285340 100644 --- a/docs/pages/templates/swift.mdx +++ b/docs/pages/templates/swift.mdx @@ -3,7 +3,7 @@ import Disclaimer from "./disclaimer.mdx"; # Swift [Swift](https://developer.apple.com/swift/) is a programming language used mainly on Apple platforms (MacOS and iOS). -[The integration is here](https://github.com/Geo-Web-Project/mud-world-base). +[See here for the Swift integration source code](https://github.com/Geo-Web-Project/mud-world-base). If you have any questions, you can ask them [in the Geo Web Discord](https://discord.com/invite/reXgPru7ck). diff --git a/docs/pages/templates/typescript/react.mdx b/docs/pages/templates/typescript/react.mdx index 4e82e045c4..fd4e5a4b77 100644 --- a/docs/pages/templates/typescript/react.mdx +++ b/docs/pages/templates/typescript/react.mdx @@ -1,6 +1,6 @@ # React Note: This page is for the new React template, which includes a todo list application. -The old React template with the counter [is explained here](./react-ecs). +[Here is the old React template with the counter](./react-ecs). **Coming soon** diff --git a/docs/pages/world/systems.mdx b/docs/pages/world/systems.mdx index 097290e05b..209ec0af64 100644 --- a/docs/pages/world/systems.mdx +++ b/docs/pages/world/systems.mdx @@ -54,6 +54,70 @@ There are two ways to call one `System` from another one. | `_msgValue()` | zero | can use [`WorldContextProvider`](https://github.com/latticexyz/mud/blob/main/packages/world/src/WorldContext.sol#L180-L203) to transfer the correct information | | Can be used by systems in the root namespace | No (it's a security measure) | Yes | +#### Calling from a root `System` + +If you need to call a `System` from a `System` in the root namespace you can use [`SystemSwitch`](https://github.com/latticexyz/mud/blob/main/packages/world-modules/src/utils/SystemSwitch.sol). + +1. Import `SystemSwitch`. + + ```solidity copy + import { SystemSwitch } from "@latticexyz/world-modules/src/utils/SystemSwitch.sol"; + ``` + +1. Import the interface for the system you wish to call. + + ```solidity copy + import { IIncrementSystem } from "../codegen/world/IIncrementSystem.sol"; + ``` + +1. Call the function using `SystemSwitch.call`. + For example, here is how you can call [`IncrementSystem.increment()`](/templates/typescript/contracts#incrementsystemsol). + + ```solidity copy + uint32 returnValue = abi.decode( + SystemSwitch.call( + abi.encodeCall(IIncrementSystem.increment, ()) + ), + (uint32) + ); + ``` + +
+ + Explanation + + ```solidity + abi.encodeCall(IIncrementSystem.increment, ()) + ``` + + Use [`abi.encodeCall`](https://docs.soliditylang.org/en/latest/cheatsheet.html#abi-encoding-and-decoding-functions) to create the calldata. + The first parameter is a pointer to the function. + The second parameter is a [tuple](https://docs.soliditylang.org/en/latest/control-structures.html#destructuring-assignments-and-returning-multiple-values) with the function parameters. + In this case, there aren't any. + + The advantage of `abi.encodeCall` is that it checks the types of the function parameters are correct. + + ```solidity + SystemSwitch.call( + abi.encodeCall(...) + ) + ``` + + Using `SystemSwitch.call` with the calldata created by `abi.encodeCall`. + `SystemSwitch.call` takes care of figuring out details, such as what type of call to use. + + ```solidity + uint32 retval = abi.decode( + SystemSwitch.call(...), + (uint32) + ); + ``` + + Use [`abi.decode`](https://docs.soliditylang.org/en/latest/cheatsheet.html#abi-encoding-and-decoding-functions) to decode the call's return value. + The second parameter is the data type (or types if there are multiple return values). + +
+ ## Registering systems For a `System` to be callable from a `World` it has to be [registered](https://github.com/latticexyz/mud/blob/main/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol#L97-L201). diff --git a/e2e/packages/contracts/src/codegen/tables/Multi.sol b/e2e/packages/contracts/src/codegen/tables/Multi.sol index a881e1403d..81b06c8aaf 100644 --- a/e2e/packages/contracts/src/codegen/tables/Multi.sol +++ b/e2e/packages/contracts/src/codegen/tables/Multi.sol @@ -382,7 +382,7 @@ library Multi { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(int256 num, bool value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(num, value); diff --git a/e2e/packages/contracts/src/codegen/tables/Number.sol b/e2e/packages/contracts/src/codegen/tables/Number.sol index 5464204518..def46cb309 100644 --- a/e2e/packages/contracts/src/codegen/tables/Number.sol +++ b/e2e/packages/contracts/src/codegen/tables/Number.sol @@ -208,7 +208,7 @@ library Number { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/e2e/packages/contracts/src/codegen/tables/NumberList.sol b/e2e/packages/contracts/src/codegen/tables/NumberList.sol index af5daed541..a0b71641e8 100644 --- a/e2e/packages/contracts/src/codegen/tables/NumberList.sol +++ b/e2e/packages/contracts/src/codegen/tables/NumberList.sol @@ -427,7 +427,7 @@ library NumberList { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32[] memory value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/e2e/packages/contracts/src/codegen/tables/Position.sol b/e2e/packages/contracts/src/codegen/tables/Position.sol index 8c501c3d16..e34da534d7 100644 --- a/e2e/packages/contracts/src/codegen/tables/Position.sol +++ b/e2e/packages/contracts/src/codegen/tables/Position.sol @@ -232,7 +232,7 @@ library Position { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address player) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(player); diff --git a/e2e/packages/contracts/src/codegen/tables/StaticArray.sol b/e2e/packages/contracts/src/codegen/tables/StaticArray.sol index 7dd01137ff..8a2b55e49e 100644 --- a/e2e/packages/contracts/src/codegen/tables/StaticArray.sol +++ b/e2e/packages/contracts/src/codegen/tables/StaticArray.sol @@ -427,7 +427,7 @@ library StaticArray { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256[2] memory value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/e2e/packages/contracts/src/codegen/tables/Vector.sol b/e2e/packages/contracts/src/codegen/tables/Vector.sol index a084416923..9bcb795d5b 100644 --- a/e2e/packages/contracts/src/codegen/tables/Vector.sol +++ b/e2e/packages/contracts/src/codegen/tables/Vector.sol @@ -328,7 +328,7 @@ library Vector { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(int32 x, int32 y) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(x, y); diff --git a/examples/minimal/packages/contracts/script/PostDeploy.s.sol b/examples/minimal/packages/contracts/script/PostDeploy.s.sol index 22fd9d5f19..661a15ba47 100644 --- a/examples/minimal/packages/contracts/script/PostDeploy.s.sol +++ b/examples/minimal/packages/contracts/script/PostDeploy.s.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.21; import { Script } from "forge-std/Script.sol"; import { console } from "forge-std/console.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; -import { ResourceId, WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; +import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; import { MessageTable, MessageTableTableId } from "../src/codegen/index.sol"; @@ -12,6 +12,8 @@ import { IWorld } from "../src/codegen/world/IWorld.sol"; import { ChatNamespacedSystem } from "../src/systems/ChatNamespacedSystem.sol"; contract PostDeploy is Script { + using WorldResourceIdInstance for ResourceId; + function run(address worldAddress) external { // Specify a store so that you can use tables directly in PostDeploy StoreSwitch.setStoreAddress(worldAddress); @@ -29,8 +31,10 @@ contract PostDeploy is Script { namespace: "namespace", name: "ChatNamespaced" }); + IWorld(worldAddress).registerNamespace(systemId.getNamespaceId()); IWorld(worldAddress).registerSystem(systemId, chatNamespacedSystem, true); IWorld(worldAddress).registerFunctionSelector(systemId, "sendMessage(string)"); + // Grant this system access to MessageTable IWorld(worldAddress).grantAccess(MessageTableTableId, address(chatNamespacedSystem)); diff --git a/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol b/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol index aaa471f58f..709f5330da 100644 --- a/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol +++ b/examples/minimal/packages/contracts/src/codegen/tables/CounterTable.sol @@ -250,7 +250,7 @@ library CounterTable { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol b/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol index 0601a0afc3..2098e55a28 100644 --- a/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol +++ b/examples/minimal/packages/contracts/src/codegen/tables/Inventory.sol @@ -232,7 +232,7 @@ library Inventory { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32 amount) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(amount); diff --git a/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol b/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol index c770e69eb2..157e866c76 100644 --- a/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol +++ b/examples/minimal/packages/contracts/src/codegen/tables/MessageTable.sol @@ -186,7 +186,7 @@ library MessageTable { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(string memory value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/examples/multiple-accounts/.eslintrc b/examples/multiple-accounts/.eslintrc new file mode 100644 index 0000000000..79bd6ef23f --- /dev/null +++ b/examples/multiple-accounts/.eslintrc @@ -0,0 +1,10 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ] +} diff --git a/examples/multiple-accounts/.gitattributes b/examples/multiple-accounts/.gitattributes new file mode 100644 index 0000000000..9c70dc52f0 --- /dev/null +++ b/examples/multiple-accounts/.gitattributes @@ -0,0 +1,3 @@ +# suppress diffs for generated files +**/pnpm-lock.yaml linguist-generated=true +**/codegen/**/*.sol linguist-generated=true diff --git a/examples/multiple-accounts/.gitignore b/examples/multiple-accounts/.gitignore new file mode 100644 index 0000000000..b512c09d47 --- /dev/null +++ b/examples/multiple-accounts/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/examples/multiple-accounts/.vscode/extensions.json b/examples/multiple-accounts/.vscode/extensions.json new file mode 100644 index 0000000000..7d5d7da3d9 --- /dev/null +++ b/examples/multiple-accounts/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["NomicFoundation.hardhat-solidity"] +} diff --git a/examples/multiple-accounts/.vscode/settings.json b/examples/multiple-accounts/.vscode/settings.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/examples/multiple-accounts/.vscode/settings.json @@ -0,0 +1 @@ +{} diff --git a/examples/multiple-accounts/mprocs.yaml b/examples/multiple-accounts/mprocs.yaml new file mode 100644 index 0000000000..89cf2bd606 --- /dev/null +++ b/examples/multiple-accounts/mprocs.yaml @@ -0,0 +1,7 @@ +procs: + contracts: + cwd: packages/contracts + shell: pnpm run dev + client: + cwd: packages/client + shell: pnpm run dev diff --git a/examples/multiple-accounts/package.json b/examples/multiple-accounts/package.json new file mode 100644 index 0000000000..efabf8e04f --- /dev/null +++ b/examples/multiple-accounts/package.json @@ -0,0 +1,29 @@ +{ + "name": "mud-template-react", + "private": true, + "scripts": { + "build": "pnpm recursive run build", + "dev": "mprocs", + "dev:client": "pnpm --filter 'client' run dev", + "dev:contracts": "pnpm --filter 'contracts' dev", + "foundry:up": "curl -L https://foundry.paradigm.xyz | bash && bash $HOME/.foundry/bin/foundryup", + "mud:up": "pnpm mud set-version --tag main && pnpm install", + "prepare": "(forge --version || pnpm foundry:up)", + "test": "pnpm recursive run test" + }, + "devDependencies": { + "@latticexyz/cli": "2.0.0-next.15", + "@types/debug": "4.1.7", + "@types/prettier": "2.7.2", + "@typescript-eslint/eslint-plugin": "5.46.1", + "@typescript-eslint/parser": "5.46.1", + "eslint": "8.29.0", + "mprocs": "^0.6.4", + "rimraf": "^3.0.2", + "typescript": "5.1.6" + }, + "engines": { + "node": "18.x", + "pnpm": "8.x" + } +} diff --git a/examples/multiple-accounts/packages/client/.eslintrc b/examples/multiple-accounts/packages/client/.eslintrc new file mode 100644 index 0000000000..930af95967 --- /dev/null +++ b/examples/multiple-accounts/packages/client/.eslintrc @@ -0,0 +1,7 @@ +{ + "extends": ["../../.eslintrc", "plugin:react/recommended", "plugin:react-hooks/recommended"], + "plugins": ["react", "react-hooks"], + "rules": { + "react/react-in-jsx-scope": "off" + } +} diff --git a/examples/multiple-accounts/packages/client/.gitignore b/examples/multiple-accounts/packages/client/.gitignore new file mode 100644 index 0000000000..0ca39c007c --- /dev/null +++ b/examples/multiple-accounts/packages/client/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +.DS_Store diff --git a/examples/multiple-accounts/packages/client/index.html b/examples/multiple-accounts/packages/client/index.html new file mode 100644 index 0000000000..fca4e978ab --- /dev/null +++ b/examples/multiple-accounts/packages/client/index.html @@ -0,0 +1,12 @@ + + + + + + Show when each address called us last + + +
+ + + diff --git a/examples/multiple-accounts/packages/client/package.json b/examples/multiple-accounts/packages/client/package.json new file mode 100644 index 0000000000..729a030305 --- /dev/null +++ b/examples/multiple-accounts/packages/client/package.json @@ -0,0 +1,37 @@ +{ + "name": "client", + "version": "0.0.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "build": "vite build", + "dev": "wait-port localhost:8545 && vite", + "preview": "vite preview", + "test": "tsc --noEmit" + }, + "dependencies": { + "@latticexyz/common": "2.0.0-next.15", + "@latticexyz/dev-tools": "2.0.0-next.15", + "@latticexyz/react": "2.0.0-next.15", + "@latticexyz/schema-type": "2.0.0-next.15", + "@latticexyz/services": "2.0.0-next.15", + "@latticexyz/store-sync": "2.0.0-next.15", + "@latticexyz/utils": "2.0.0-next.15", + "@latticexyz/world": "2.0.0-next.15", + "contracts": "workspace:*", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "rxjs": "7.5.5", + "viem": "1.14.0" + }, + "devDependencies": { + "@types/react": "18.2.22", + "@types/react-dom": "18.2.7", + "@vitejs/plugin-react": "^3.1.0", + "eslint-plugin-react": "7.31.11", + "eslint-plugin-react-hooks": "4.6.0", + "vite": "^4.2.1", + "wait-port": "^1.0.4" + } +} diff --git a/examples/multiple-accounts/packages/client/src/App.tsx b/examples/multiple-accounts/packages/client/src/App.tsx new file mode 100644 index 0000000000..f3d5e4bf4e --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/App.tsx @@ -0,0 +1,110 @@ +import { useMUD } from "./MUDContext"; +import { getBurnerPrivateKey, createBurnerAccount, transportObserver, getContract } from "@latticexyz/common"; +import { createPublicClient, createWalletClient, fallback, webSocket, http, ClientConfig, Hex } from "viem"; +import { getNetworkConfig } from "./mud/getNetworkConfig"; +import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; + +// A lot of code that would normally go in mud/getNetworkConfig.ts is part of the application here, +// because `getNetworkConfig.ts` assumes you use one wallet, and here we need several. +const networkConfig = await getNetworkConfig(); + +const clientOptions = { + chain: networkConfig.chain, + transport: transportObserver(fallback([webSocket(), http()])), + pollingInterval: 1000, +} as const satisfies ClientConfig; + +const publicClient = createPublicClient({ + ...clientOptions, +}); + +// Create a structure with two fields: +// client - a wallet client that uses a random account +// world - a world contract object that lets us issue newCall +const makeWorldContract = () => { + const client = createWalletClient({ + ...clientOptions, + account: createBurnerAccount(getBurnerPrivateKey(Math.random().toString())), + }); + + return { + world: getContract({ + address: networkConfig.worldAddress as Hex, + abi: IWorldAbi, + publicClient: publicClient, + walletClient: client, + }), + client, + }; +}; + +// Create five world contracts +const worldContracts = [1, 2, 3, 4, 5].map((x) => makeWorldContract()); + +export const App = () => { + const { + network: { tables, useStore }, + } = useMUD(); + + // Get all the calls from the records cache. + const calls = useStore((state) => { + const records = Object.values(state.getRecords(tables.LastCall)); + records.sort((a, b) => Number(a.value.callTime - b.value.callTime)); + return records; + }); + + // Convert timestamps to readable format + const twoDigit = (str) => str.toString().padStart(2, "0"); + const timestamp2Str = (timestamp: number) => { + const date = new Date(timestamp * 1000); + return `${twoDigit(date.getHours())}:${twoDigit(date.getMinutes())}:${twoDigit(date.getSeconds())}`; + }; + + // Call newCall() on LastCall:LastCallSystem. + const newCall = async (worldContract) => { + const tx = await worldContract.write.LastCall_LastCallSystem_newCall(); + }; + + return ( + <> +

Last calls

+ + + + + + + { + // Show all the calls + calls.map((call) => ( + + + + + )) + } + +
CallerTime
{call.key.caller}{timestamp2Str(Number(call.value.callTime))}
+

My clients

+ { + // For every world contract, have a button to call newCall as that address. + worldContracts.map((worldContract) => ( + <> + +
+ + )) + } + + ); +}; diff --git a/examples/multiple-accounts/packages/client/src/MUDContext.tsx b/examples/multiple-accounts/packages/client/src/MUDContext.tsx new file mode 100644 index 0000000000..7b5637f6a6 --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/MUDContext.tsx @@ -0,0 +1,21 @@ +import { createContext, ReactNode, useContext } from "react"; +import { SetupResult } from "./mud/setup"; + +const MUDContext = createContext(null); + +type Props = { + children: ReactNode; + value: SetupResult; +}; + +export const MUDProvider = ({ children, value }: Props) => { + const currentValue = useContext(MUDContext); + if (currentValue) throw new Error("MUDProvider can only be used once"); + return {children}; +}; + +export const useMUD = () => { + const value = useContext(MUDContext); + if (!value) throw new Error("Must be used within a MUDProvider"); + return value; +}; diff --git a/examples/multiple-accounts/packages/client/src/index.tsx b/examples/multiple-accounts/packages/client/src/index.tsx new file mode 100644 index 0000000000..3ba2a44052 --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/index.tsx @@ -0,0 +1,34 @@ +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"); +const root = ReactDOM.createRoot(rootElement); + +// TODO: figure out if we actually want this to be async or if we should render something else in the meantime +setup().then(async (result) => { + root.render( + + + + ); + + // 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/multiple-accounts/packages/client/src/mud/createSystemCalls.ts b/examples/multiple-accounts/packages/client/src/mud/createSystemCalls.ts new file mode 100644 index 0000000000..0b8c753a67 --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/mud/createSystemCalls.ts @@ -0,0 +1,13 @@ +/* + * Create the system calls that the client can use to ask + * for changes in the World state (using the System contracts). + */ + +import { Hex } from "viem"; +import { SetupNetworkResult } from "./setupNetwork"; + +export type SystemCalls = ReturnType; + +export function createSystemCalls({ tables, useStore, worldContract, waitForTransaction }: SetupNetworkResult) { + return {}; +} diff --git a/examples/multiple-accounts/packages/client/src/mud/getNetworkConfig.ts b/examples/multiple-accounts/packages/client/src/mud/getNetworkConfig.ts new file mode 100644 index 0000000000..61ad962f25 --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/mud/getNetworkConfig.ts @@ -0,0 +1,91 @@ +/* + * Network specific configuration for the client. + * By default connect to the anvil test network. + * + */ + +/* + * By default the template just creates a temporary wallet + * (called a burner wallet) and uses a faucet (on our test net) + * to get ETH for it. + * + * See https://mud.dev/tutorials/minimal/deploy#wallet-managed-address + * for how to use the user's own address instead. + */ +import { getBurnerPrivateKey } from "@latticexyz/common"; + +/* + * Import the addresses of the World, possibly on multiple chains, + * from packages/contracts/worlds.json. When the contracts package + * deploys a new `World`, it updates this file. + */ +import worlds from "contracts/worlds.json"; + +/* + * The supported chains. + * By default, there are only two chains here: + * + * - mudFoundry, the chain running on anvil that pnpm dev + * starts by default. It is similar to the viem anvil chain + * (see https://viem.sh/docs/clients/test.html), but with the + * basefee set to zero to avoid transaction fees. + * - latticeTestnet, our public test network. + * + * See https://mud.dev/tutorials/minimal/deploy#run-the-user-interface + * for instructions on how to add networks. + */ +import { supportedChains } from "./supportedChains"; + +export async function getNetworkConfig() { + const params = new URLSearchParams(window.location.search); + + /* + * The chain ID is the first item available from this list: + * 1. chainId query parameter + * 2. chainid query parameter + * 3. The VITE_CHAIN_ID environment variable set when the + * vite dev server was started or client was built + * 4. The default, 31337 (anvil) + */ + const chainId = Number(params.get("chainId") || params.get("chainid") || import.meta.env.VITE_CHAIN_ID || 31337); + + /* + * Find the chain (unless it isn't in the list of supported chains). + */ + const chainIndex = supportedChains.findIndex((c) => c.id === chainId); + const chain = supportedChains[chainIndex]; + if (!chain) { + throw new Error(`Chain ${chainId} not found`); + } + + /* + * Get the address of the World. If you want to use a + * different address than the one in worlds.json, + * provide it as worldAddress in the query string. + */ + const world = worlds[chain.id.toString()]; + const worldAddress = params.get("worldAddress") || world?.address; + if (!worldAddress) { + throw new Error(`No world address found for chain ${chainId}. Did you run \`mud deploy\`?`); + } + + /* + * MUD clients use events to synchronize the database, meaning + * they need to look as far back as when the World was started. + * The block number for the World start can be specified either + * on the URL (as initialBlockNumber) or in the worlds.json + * file. If neither has it, it starts at the first block, zero. + */ + const initialBlockNumber = params.has("initialBlockNumber") + ? Number(params.get("initialBlockNumber")) + : world?.blockNumber ?? 0n; + + return { + privateKey: getBurnerPrivateKey(), + chainId, + chain, + faucetServiceUrl: params.get("faucet") ?? chain.faucetUrl, + worldAddress, + initialBlockNumber, + }; +} diff --git a/examples/multiple-accounts/packages/client/src/mud/setup.ts b/examples/multiple-accounts/packages/client/src/mud/setup.ts new file mode 100644 index 0000000000..6d07134612 --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/mud/setup.ts @@ -0,0 +1,18 @@ +/* + * This file sets up all the definitions required for a MUD client. + */ + +import { createSystemCalls } from "./createSystemCalls"; +import { setupNetwork } from "./setupNetwork"; + +export type SetupResult = Awaited>; + +export async function setup() { + const network = await setupNetwork(); + const systemCalls = createSystemCalls(network); + + return { + network, + systemCalls, + }; +} diff --git a/examples/multiple-accounts/packages/client/src/mud/setupNetwork.ts b/examples/multiple-accounts/packages/client/src/mud/setupNetwork.ts new file mode 100644 index 0000000000..5cd26c31b1 --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/mud/setupNetwork.ts @@ -0,0 +1,120 @@ +/* + * The MUD client code is built on top of viem + * (https://viem.sh/docs/getting-started.html). + * This line imports the functions we need from it. + */ +import { createPublicClient, fallback, webSocket, http, createWalletClient, Hex, parseEther, ClientConfig } from "viem"; +import { createFaucetService } from "@latticexyz/services/faucet"; +import { syncToZustand } from "@latticexyz/store-sync/zustand"; +import { getNetworkConfig } from "./getNetworkConfig"; +import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; +import { createBurnerAccount, getContract, transportObserver, ContractWrite } from "@latticexyz/common"; +import { Subject, share } from "rxjs"; + +/* + * Import our MUD config, which includes strong types for + * our tables and other config options. We use this to generate + * things like RECS components and get back strong types for them. + * + * See https://mud.dev/templates/typescript/contracts#mudconfigts + * for the source of this information. + */ +import mudConfig from "contracts/mud.config"; + +export type SetupNetworkResult = Awaited>; + +export async function setupNetwork() { + const networkConfig = await getNetworkConfig(); + + /* + * Create a viem public (read only) client + * (https://viem.sh/docs/clients/public.html) + */ + const clientOptions = { + chain: networkConfig.chain, + transport: transportObserver(fallback([webSocket(), http()])), + pollingInterval: 1000, + } as const satisfies ClientConfig; + + const publicClient = createPublicClient(clientOptions); + + /* + * Create a temporary wallet and a viem client for it + * (see https://viem.sh/docs/clients/wallet.html). + */ + const burnerAccount = createBurnerAccount(networkConfig.privateKey as Hex); + const burnerWalletClient = createWalletClient({ + ...clientOptions, + account: burnerAccount, + }); + + /* + * Create an observable for contract writes that we can + * pass into MUD dev tools for transaction observability. + */ + const write$ = new Subject(); + + /* + * Create an object for communicating with the deployed World. + */ + const worldContract = getContract({ + address: networkConfig.worldAddress as Hex, + abi: IWorldAbi, + publicClient, + walletClient: burnerWalletClient, + onWrite: (write) => write$.next(write), + }); + + /* + * Sync on-chain state into RECS and keeps our client in sync. + * Uses the MUD indexer if available, otherwise falls back + * to the viem publicClient to make RPC calls to fetch MUD + * events from the chain. + */ + const { tables, useStore, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToZustand({ + config: mudConfig, + address: networkConfig.worldAddress as Hex, + publicClient, + startBlock: BigInt(networkConfig.initialBlockNumber), + }); + + /* + * If there is a faucet, request (test) ETH if you have + * less than 1 ETH. Repeat every 20 seconds to ensure you don't + * run out. + */ + if (networkConfig.faucetServiceUrl) { + const address = burnerAccount.address; + console.info("[Dev Faucet]: Player address -> ", address); + + const faucet = createFaucetService(networkConfig.faucetServiceUrl); + + const requestDrip = async () => { + const balance = await publicClient.getBalance({ address }); + console.info(`[Dev Faucet]: Player balance -> ${balance}`); + const lowBalance = balance < parseEther("1"); + if (lowBalance) { + console.info("[Dev Faucet]: Balance is low, dripping funds to player"); + // Double drip + await faucet.dripDev({ address }); + await faucet.dripDev({ address }); + } + }; + + requestDrip(); + // Request a drip every 20 seconds + setInterval(requestDrip, 20000); + } + + return { + tables, + useStore, + publicClient, + walletClient: burnerWalletClient, + latestBlock$, + storedBlockLogs$, + waitForTransaction, + worldContract, + write$: write$.asObservable().pipe(share()), + }; +} diff --git a/examples/multiple-accounts/packages/client/src/mud/supportedChains.ts b/examples/multiple-accounts/packages/client/src/mud/supportedChains.ts new file mode 100644 index 0000000000..614412500e --- /dev/null +++ b/examples/multiple-accounts/packages/client/src/mud/supportedChains.ts @@ -0,0 +1,20 @@ +/* + * The supported chains. + * By default, there are only two chains here: + * + * - mudFoundry, the chain running on anvil that pnpm dev + * starts by default. It is similar to the viem anvil chain + * (see https://viem.sh/docs/clients/test.html), but with the + * basefee set to zero to avoid transaction fees. + * - latticeTestnet, our public test network. + * + + */ + +import { MUDChain, latticeTestnet, mudFoundry } from "@latticexyz/common/chains"; + +/* + * See https://mud.dev/tutorials/minimal/deploy#run-the-user-interface + * for instructions on how to add networks. + */ +export const supportedChains: MUDChain[] = [mudFoundry, latticeTestnet]; diff --git a/examples/multiple-accounts/packages/client/tsconfig.json b/examples/multiple-accounts/packages/client/tsconfig.json new file mode 100644 index 0000000000..5bf43bace8 --- /dev/null +++ b/examples/multiple-accounts/packages/client/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "types": ["vite/client"], + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "jsxImportSource": "react" + }, + "include": ["src"] +} diff --git a/examples/multiple-accounts/packages/client/vite.config.ts b/examples/multiple-accounts/packages/client/vite.config.ts new file mode 100644 index 0000000000..a47d70e7a1 --- /dev/null +++ b/examples/multiple-accounts/packages/client/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3000, + fs: { + strict: false, + }, + }, + build: { + target: "es2022", + minify: true, + sourcemap: true, + }, +}); diff --git a/examples/multiple-accounts/packages/contracts/.gitignore b/examples/multiple-accounts/packages/contracts/.gitignore new file mode 100644 index 0000000000..aea4e54c82 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/.gitignore @@ -0,0 +1,9 @@ +out/ +cache/ +node_modules/ +bindings/ +artifacts/ +broadcast/ + +# Ignore MUD deploy artifacts +deploys/**/*.json diff --git a/examples/multiple-accounts/packages/contracts/.prettierrc b/examples/multiple-accounts/packages/contracts/.prettierrc new file mode 100644 index 0000000000..bf689e8c48 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/.prettierrc @@ -0,0 +1,8 @@ +{ + "plugins": ["prettier-plugin-solidity"], + "printWidth": 120, + "semi": true, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true +} diff --git a/examples/multiple-accounts/packages/contracts/.solhint.json b/examples/multiple-accounts/packages/contracts/.solhint.json new file mode 100644 index 0000000000..f3e0b01ffc --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/.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/multiple-accounts/packages/contracts/foundry.toml b/examples/multiple-accounts/packages/contracts/foundry.toml new file mode 100644 index 0000000000..d03fad8043 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/foundry.toml @@ -0,0 +1,26 @@ +[profile.default] +solc = "0.8.21" +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.lattice-testnet] +eth_rpc_url = "https://follower.testnet-chain.linfra.xyz" diff --git a/examples/multiple-accounts/packages/contracts/mud.config.ts b/examples/multiple-accounts/packages/contracts/mud.config.ts new file mode 100644 index 0000000000..3ba46294c4 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/mud.config.ts @@ -0,0 +1,15 @@ +import { mudConfig } from "@latticexyz/world/register"; + +export default mudConfig({ + namespace: "LastCall", + tables: { + LastCall: { + keySchema: { + caller: "address", + }, + valueSchema: { + callTime: "uint256", + }, + }, + }, +}); diff --git a/examples/multiple-accounts/packages/contracts/package.json b/examples/multiple-accounts/packages/contracts/package.json new file mode 100644 index 0000000000..78dd852e30 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/package.json @@ -0,0 +1,34 @@ +{ + "name": "contracts", + "version": "0.0.0", + "private": true, + "license": "MIT", + "scripts": { + "build": "mud build", + "clean": "forge clean && rimraf src/codegen", + "deploy:local": "pnpm run build && mud deploy", + "deploy:testnet": "pnpm run build && mud deploy --profile=lattice-testnet", + "dev": "pnpm 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/cli": "2.0.0-next.15", + "@latticexyz/schema-type": "2.0.0-next.15", + "@latticexyz/store": "2.0.0-next.15", + "@latticexyz/world": "2.0.0-next.15", + "@latticexyz/world-modules": "2.0.0-next.15" + }, + "devDependencies": { + "@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": "^2.6.2", + "prettier-plugin-solidity": "1.1.3", + "solhint": "^3.3.7", + "solhint-config-mud": "2.0.0-next.15", + "solhint-plugin-mud": "2.0.0-next.15" + } +} diff --git a/examples/multiple-accounts/packages/contracts/remappings.txt b/examples/multiple-accounts/packages/contracts/remappings.txt new file mode 100644 index 0000000000..c4d992480e --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/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/multiple-accounts/packages/contracts/script/PostDeploy.s.sol b/examples/multiple-accounts/packages/contracts/script/PostDeploy.s.sol new file mode 100644 index 0000000000..de01099e0a --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/script/PostDeploy.s.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +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 { LastCall } 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); + + // Add a last call + IWorld(worldAddress).LastCall_LastCallSystem_newCall(); + + vm.stopBroadcast(); + } +} diff --git a/examples/multiple-accounts/packages/contracts/src/codegen/index.sol b/examples/multiple-accounts/packages/contracts/src/codegen/index.sol new file mode 100644 index 0000000000..525fad9dae --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/src/codegen/index.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +/* Autogenerated file. Do not edit manually. */ + +import { LastCall, LastCallTableId } from "./tables/LastCall.sol"; diff --git a/examples/multiple-accounts/packages/contracts/src/codegen/tables/LastCall.sol b/examples/multiple-accounts/packages/contracts/src/codegen/tables/LastCall.sol new file mode 100644 index 0000000000..f22ae9bd64 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/src/codegen/tables/LastCall.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +/* Autogenerated file. Do not edit manually. */ + +// Import schema type +import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; + +// 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, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol"; +import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; +import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "@latticexyz/store/src/storeResourceTypes.sol"; + +ResourceId constant _tableId = ResourceId.wrap( + bytes32(abi.encodePacked(RESOURCE_TABLE, bytes14("LastCall"), bytes16("LastCall"))) +); +ResourceId constant LastCallTableId = _tableId; + +FieldLayout constant _fieldLayout = FieldLayout.wrap( + 0x0020010020000000000000000000000000000000000000000000000000000000 +); + +library LastCall { + /** + * @notice Get the table values' field layout. + * @return _fieldLayout The field layout for the table. + */ + function getFieldLayout() internal pure returns (FieldLayout) { + return _fieldLayout; + } + + /** + * @notice Get the table's key schema. + * @return _keySchema The key schema for the table. + */ + function getKeySchema() internal pure returns (Schema) { + SchemaType[] memory _keySchema = new SchemaType[](1); + _keySchema[0] = SchemaType.ADDRESS; + + return SchemaLib.encode(_keySchema); + } + + /** + * @notice Get the table's value schema. + * @return _valueSchema The value schema for the table. + */ + function getValueSchema() internal pure returns (Schema) { + SchemaType[] memory _valueSchema = new SchemaType[](1); + _valueSchema[0] = SchemaType.UINT256; + + return SchemaLib.encode(_valueSchema); + } + + /** + * @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] = "caller"; + } + + /** + * @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[](1); + fieldNames[0] = "callTime"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames()); + } + + /** + * @notice Get callTime. + */ + function getCallTime(address caller) internal view returns (uint256 callTime) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get callTime. + */ + function _getCallTime(address caller) internal view returns (uint256 callTime) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get callTime. + */ + function get(address caller) internal view returns (uint256 callTime) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get callTime. + */ + function _get(address caller) internal view returns (uint256 callTime) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Set callTime. + */ + function setCallTime(address caller, uint256 callTime) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((callTime)), _fieldLayout); + } + + /** + * @notice Set callTime. + */ + function _setCallTime(address caller, uint256 callTime) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((callTime)), _fieldLayout); + } + + /** + * @notice Set callTime. + */ + function set(address caller, uint256 callTime) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((callTime)), _fieldLayout); + } + + /** + * @notice Set callTime. + */ + function _set(address caller, uint256 callTime) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((callTime)), _fieldLayout); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(address caller) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(address caller) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + 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 callTime) internal pure returns (bytes memory) { + return abi.encodePacked(callTime); + } + + /** + * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + */ + function encode(uint256 callTime) internal pure returns (bytes memory, PackedCounter, bytes memory) { + bytes memory _staticData = encodeStatic(callTime); + + PackedCounter _encodedLengths; + bytes memory _dynamicData; + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(address caller) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = bytes32(uint256(uint160(caller))); + + return _keyTuple; + } +} diff --git a/examples/multiple-accounts/packages/contracts/src/codegen/world/ILastCallSystem.sol b/examples/multiple-accounts/packages/contracts/src/codegen/world/ILastCallSystem.sol new file mode 100644 index 0000000000..ea46b83c17 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/src/codegen/world/ILastCallSystem.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +/* Autogenerated file. Do not edit manually. */ + +/** + * @title ILastCallSystem + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + */ +interface ILastCallSystem { + function LastCall_LastCallSystem_newCall() external; +} diff --git a/examples/multiple-accounts/packages/contracts/src/codegen/world/ITasksSystem.sol b/examples/multiple-accounts/packages/contracts/src/codegen/world/ITasksSystem.sol new file mode 100644 index 0000000000..e9bfddc79e --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/src/codegen/world/ITasksSystem.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +/* Autogenerated file. Do not edit manually. */ + +/** + * @title ITasksSystem + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + */ +interface ITasksSystem { + function addTask(string memory description) external returns (bytes32 key); + + function completeTask(bytes32 key) external; + + function resetTask(bytes32 key) external; + + function deleteTask(bytes32 key) external; +} diff --git a/examples/multiple-accounts/packages/contracts/src/codegen/world/IWorld.sol b/examples/multiple-accounts/packages/contracts/src/codegen/world/IWorld.sol new file mode 100644 index 0000000000..35618e1257 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/src/codegen/world/IWorld.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +/* Autogenerated file. Do not edit manually. */ + +import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; + +import { ILastCallSystem } from "./ILastCallSystem.sol"; + +/** + * @title IWorld + * @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, ILastCallSystem { + +} diff --git a/examples/multiple-accounts/packages/contracts/src/systems/LastCallSystem.sol b/examples/multiple-accounts/packages/contracts/src/systems/LastCallSystem.sol new file mode 100644 index 0000000000..e40df89975 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/src/systems/LastCallSystem.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { System } from "@latticexyz/world/src/System.sol"; +import { LastCall } from "../codegen/index.sol"; + +contract LastCallSystem is System { + function newCall() public { + LastCall.set(_msgSender(), block.timestamp); + } +} diff --git a/examples/multiple-accounts/packages/contracts/tsconfig.json b/examples/multiple-accounts/packages/contracts/tsconfig.json new file mode 100644 index 0000000000..270db8fdf6 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/tsconfig.json @@ -0,0 +1,13 @@ +// Visit https://aka.ms/tsconfig.json for all config options +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "strict": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node" + } +} diff --git a/examples/multiple-accounts/packages/contracts/worlds.json b/examples/multiple-accounts/packages/contracts/worlds.json new file mode 100644 index 0000000000..906845ce6d --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/worlds.json @@ -0,0 +1,5 @@ +{ + "31337": { + "address": "0xc44504ab6a2c4df9a9ce82aecfc453fec3c8771c" + } +} \ No newline at end of file diff --git a/examples/multiple-accounts/packages/contracts/worlds.json.d.ts b/examples/multiple-accounts/packages/contracts/worlds.json.d.ts new file mode 100644 index 0000000000..90ffc786f4 --- /dev/null +++ b/examples/multiple-accounts/packages/contracts/worlds.json.d.ts @@ -0,0 +1,2 @@ +declare const worlds: Partial>; +export default worlds; diff --git a/examples/multiple-accounts/pnpm-lock.yaml b/examples/multiple-accounts/pnpm-lock.yaml new file mode 100644 index 0000000000..3a42631ed8 --- /dev/null +++ b/examples/multiple-accounts/pnpm-lock.yaml @@ -0,0 +1,4323 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@latticexyz/cli': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(google-protobuf@3.21.2) + '@types/debug': + specifier: 4.1.7 + version: 4.1.7 + '@types/prettier': + specifier: 2.7.2 + version: 2.7.2 + '@typescript-eslint/eslint-plugin': + specifier: 5.46.1 + version: 5.46.1(@typescript-eslint/parser@5.46.1)(eslint@8.29.0)(typescript@5.1.6) + '@typescript-eslint/parser': + specifier: 5.46.1 + version: 5.46.1(eslint@8.29.0)(typescript@5.1.6) + eslint: + specifier: 8.29.0 + version: 8.29.0 + mprocs: + specifier: ^0.6.4 + version: 0.6.4 + rimraf: + specifier: ^3.0.2 + version: 3.0.2 + typescript: + specifier: 5.1.6 + version: 5.1.6 + + packages/client: + dependencies: + '@latticexyz/common': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/dev-tools': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(@latticexyz/common@2.0.0-next.15)(@latticexyz/recs@2.0.0-next.15)(@latticexyz/store-sync@2.0.0-next.15)(@latticexyz/store@2.0.0-next.15)(@latticexyz/utils@2.0.0-next.15)(@latticexyz/world@2.0.0-next.15)(@types/react@18.2.22)(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/react': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/schema-type': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/services': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(google-protobuf@3.21.2) + '@latticexyz/store-sync': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(@types/react@18.2.22)(react@18.2.0)(typescript@5.1.6) + '@latticexyz/utils': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15 + '@latticexyz/world': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6) + contracts: + specifier: workspace:* + version: link:../contracts + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + rxjs: + specifier: 7.5.5 + version: 7.5.5 + viem: + specifier: 1.14.0 + version: 1.14.0(typescript@5.1.6)(zod@3.22.4) + devDependencies: + '@types/react': + specifier: 18.2.22 + version: 18.2.22 + '@types/react-dom': + specifier: 18.2.7 + version: 18.2.7 + '@vitejs/plugin-react': + specifier: ^3.1.0 + version: 3.1.0(vite@4.5.1) + eslint-plugin-react: + specifier: 7.31.11 + version: 7.31.11(eslint@8.29.0) + eslint-plugin-react-hooks: + specifier: 4.6.0 + version: 4.6.0(eslint@8.29.0) + vite: + specifier: ^4.2.1 + version: 4.5.1 + wait-port: + specifier: ^1.0.4 + version: 1.1.0 + + packages/contracts: + dependencies: + '@latticexyz/cli': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(google-protobuf@3.21.2) + '@latticexyz/schema-type': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/store': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/world': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/world-modules': + specifier: 2.0.0-next.15 + version: 2.0.0-next.15(typescript@5.1.6) + devDependencies: + '@types/node': + specifier: ^18.15.11 + version: 18.19.6 + ds-test: + specifier: https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0 + version: github.com/dapphub/ds-test/e282159d5170298eb2455a6c05280ab5a73a4ef0 + forge-std: + specifier: https://github.com/foundry-rs/forge-std.git#74cfb77e308dd188d2f58864aaf44963ae6b88b1 + version: github.com/foundry-rs/forge-std/74cfb77e308dd188d2f58864aaf44963ae6b88b1 + prettier: + specifier: ^2.6.2 + version: 2.8.8 + prettier-plugin-solidity: + specifier: 1.1.3 + version: 1.1.3(prettier@2.8.8) + solhint: + specifier: ^3.3.7 + version: 3.6.2(typescript@5.1.6) + solhint-config-mud: + specifier: 2.0.0-next.15 + version: 2.0.0-next.15 + solhint-plugin-mud: + specifier: 2.0.0-next.15 + version: 2.0.0-next.15 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@adraffy/ens-normalize@1.9.4: + resolution: {integrity: sha512-UK0bHA7hh9cR39V+4gl2/NnBBjoXIxkuWAPCaY4X7fbH4L/azIi7ilWOCjMUYfpJgraLUAqkRi2BqrjME8Rynw==} + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.7: + resolution: {integrity: sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7) + '@babel/helpers': 7.23.8 + '@babel/parser': 7.23.6 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.7 + '@babel/types': 7.23.6 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.22.2 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.7): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.7 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.8: + resolution: {integrity: sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.7 + '@babel/types': 7.23.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.6: + resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.6 + dev: true + + /@babel/plugin-transform-react-jsx-self@7.23.3(@babel/core@7.23.7): + resolution: {integrity: sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.7 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-react-jsx-source@7.23.3(@babel/core@7.23.7): + resolution: {integrity: sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.7 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + dev: true + + /@babel/traverse@7.23.7: + resolution: {integrity: sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.6: + resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@esbuild/android-arm64@0.17.19: + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.17.19: + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.17.19: + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.17.19: + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.17.19: + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.17.19: + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.17.19: + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.17.19: + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.17.19: + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.17.19: + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.17.19: + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.17.19: + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.17.19: + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.17.19: + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.17.19: + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.17.19: + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.17.19: + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.17.19: + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.17.19: + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.17.19: + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.17.19: + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint/eslintrc@1.4.1: + resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@ethersproject/abi@5.7.0: + resolution: {integrity: sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==} + dependencies: + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + + /@ethersproject/abstract-provider@5.7.0: + resolution: {integrity: sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/properties': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/web': 5.7.1 + + /@ethersproject/abstract-signer@5.7.0: + resolution: {integrity: sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==} + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + + /@ethersproject/address@5.7.0: + resolution: {integrity: sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/rlp': 5.7.0 + + /@ethersproject/base64@5.7.0: + resolution: {integrity: sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==} + dependencies: + '@ethersproject/bytes': 5.7.0 + + /@ethersproject/basex@5.7.0: + resolution: {integrity: sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/properties': 5.7.0 + + /@ethersproject/bignumber@5.7.0: + resolution: {integrity: sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + bn.js: 5.2.1 + + /@ethersproject/bytes@5.7.0: + resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} + dependencies: + '@ethersproject/logger': 5.7.0 + + /@ethersproject/constants@5.7.0: + resolution: {integrity: sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + + /@ethersproject/contracts@5.7.0: + resolution: {integrity: sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==} + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/transactions': 5.7.0 + + /@ethersproject/hash@5.7.0: + resolution: {integrity: sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==} + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + + /@ethersproject/hdnode@5.7.0: + resolution: {integrity: sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==} + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/wordlists': 5.7.0 + + /@ethersproject/json-wallets@5.7.0: + resolution: {integrity: sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==} + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + aes-js: 3.0.0 + scrypt-js: 3.0.1 + + /@ethersproject/keccak256@5.7.0: + resolution: {integrity: sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==} + dependencies: + '@ethersproject/bytes': 5.7.0 + js-sha3: 0.8.0 + + /@ethersproject/logger@5.7.0: + resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} + + /@ethersproject/networks@5.7.1: + resolution: {integrity: sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==} + dependencies: + '@ethersproject/logger': 5.7.0 + + /@ethersproject/pbkdf2@5.7.0: + resolution: {integrity: sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/sha2': 5.7.0 + + /@ethersproject/properties@5.7.0: + resolution: {integrity: sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==} + dependencies: + '@ethersproject/logger': 5.7.0 + + /@ethersproject/providers@5.7.2: + resolution: {integrity: sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==} + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/web': 5.7.1 + bech32: 1.1.4 + ws: 7.4.6 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + /@ethersproject/random@5.7.0: + resolution: {integrity: sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + + /@ethersproject/rlp@5.7.0: + resolution: {integrity: sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + + /@ethersproject/sha2@5.7.0: + resolution: {integrity: sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + hash.js: 1.1.7 + + /@ethersproject/signing-key@5.7.0: + resolution: {integrity: sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + bn.js: 5.2.1 + elliptic: 6.5.4 + hash.js: 1.1.7 + + /@ethersproject/solidity@5.7.0: + resolution: {integrity: sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/strings': 5.7.0 + + /@ethersproject/strings@5.7.0: + resolution: {integrity: sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + + /@ethersproject/transactions@5.7.0: + resolution: {integrity: sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==} + dependencies: + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + + /@ethersproject/units@5.7.0: + resolution: {integrity: sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + + /@ethersproject/wallet@5.7.0: + resolution: {integrity: sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==} + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/json-wallets': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/wordlists': 5.7.0 + + /@ethersproject/web@5.7.1: + resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} + dependencies: + '@ethersproject/base64': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + + /@ethersproject/wordlists@5.7.0: + resolution: {integrity: sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@improbable-eng/grpc-web-node-http-transport@0.15.0(@improbable-eng/grpc-web@0.15.0): + resolution: {integrity: sha512-HLgJfVolGGpjc9DWPhmMmXJx8YGzkek7jcCFO1YYkSOoO81MWRZentPOd/JiKiZuU08wtc4BG+WNuGzsQB5jZA==} + peerDependencies: + '@improbable-eng/grpc-web': '>=0.13.0' + dependencies: + '@improbable-eng/grpc-web': 0.15.0(google-protobuf@3.21.2) + + /@improbable-eng/grpc-web@0.15.0(google-protobuf@3.21.2): + resolution: {integrity: sha512-ERft9/0/8CmYalqOVnJnpdDry28q+j+nAlFFARdjyxXDJ+Mhgv9+F600QC8BR9ygOfrXRlAk6CvST2j+JCpQPg==} + peerDependencies: + google-protobuf: ^3.14.0 + dependencies: + browser-headers: 0.4.1 + google-protobuf: 3.21.2 + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@latticexyz/abi-ts@2.0.0-next.15: + resolution: {integrity: sha512-/YJi9Xjstn24APqHCgUZj5QZhFeuD6TNR0fEnfKfr4GQ90Clv4Lr63Rhk847NdA/eI3c1rKfNWbFc6pJYFYwYQ==} + hasBin: true + dependencies: + chalk: 5.3.0 + debug: 4.3.4 + execa: 7.2.0 + glob: 8.1.0 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + + /@latticexyz/block-logs-stream@2.0.0-next.15(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-OcvbBT3oOQVNJHfIX89a6KFUn6X3WFisKoG2Q0yYANdyFWPfoNYopep1h4RMzvKGebuN+XQ/ClHIsrupcAuxZA==} + dependencies: + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + abitype: 0.9.8(typescript@5.1.6)(zod@3.22.4) + debug: 4.3.4 + rxjs: 7.5.5 + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + dev: false + + /@latticexyz/cli@2.0.0-next.15(google-protobuf@3.21.2): + resolution: {integrity: sha512-q10NkBxDwnTK+6vIbHgwpeYlo/aD/3h6E8ZyzdAl8GgYLtDmZ+XJyOJwj0Bss6BdzjD9gThfMQ/wFVfGGvgung==} + hasBin: true + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@improbable-eng/grpc-web': 0.15.0(google-protobuf@3.21.2) + '@improbable-eng/grpc-web-node-http-transport': 0.15.0(@improbable-eng/grpc-web@0.15.0) + '@latticexyz/abi-ts': 2.0.0-next.15 + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/config': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/gas-report': 2.0.0-next.15 + '@latticexyz/protocol-parser': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/services': 2.0.0-next.15(google-protobuf@3.21.2) + '@latticexyz/store': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/utils': 2.0.0-next.15 + '@latticexyz/world': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/world-modules': 2.0.0-next.15(typescript@5.1.6) + chalk: 5.3.0 + chokidar: 3.5.3 + debug: 4.3.4 + dotenv: 16.3.1 + ejs: 3.1.9 + ethers: 5.7.2 + execa: 7.2.0 + glob: 8.1.0 + nice-grpc-web: 2.0.2(google-protobuf@3.21.2) + openurl: 1.1.1 + p-retry: 5.1.2 + path: 0.12.7 + rxjs: 7.5.5 + throttle-debounce: 5.0.0 + typescript: 5.1.6 + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + yargs: 17.7.2 + zod: 3.22.4 + zod-validation-error: 1.5.0(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - google-protobuf + - supports-color + - utf-8-validate + + /@latticexyz/common@2.0.0-next.15(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-BncRMgjVqd0jbWtNBFUDk/RMWV+/LRcQAZAY2yWWuoVg7SZfX2ZrSSMOuf/V04TNJK2gKd6GVdRFF6Hjdi5C0A==} + dependencies: + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@solidity-parser/parser': 0.16.2 + debug: 4.3.4 + execa: 7.2.0 + p-queue: 7.4.1 + p-retry: 5.1.2 + prettier: 2.8.8 + prettier-plugin-solidity: 1.1.3(prettier@2.8.8) + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + + /@latticexyz/config@2.0.0-next.15(typescript@5.1.6): + resolution: {integrity: sha512-dHcOJydgIBo4F6eK0bpo8JspE1kZN7TpXU0DceANlKTDUBg+KtSsSeGNDJBT3Ch0nJGeW0RXdA7LuiN6UIEG/Q==} + dependencies: + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + esbuild: 0.17.19 + find-up: 6.3.0 + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + zod: 3.22.4 + zod-validation-error: 1.5.0(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + + /@latticexyz/dev-tools@2.0.0-next.15(@latticexyz/common@2.0.0-next.15)(@latticexyz/recs@2.0.0-next.15)(@latticexyz/store-sync@2.0.0-next.15)(@latticexyz/store@2.0.0-next.15)(@latticexyz/utils@2.0.0-next.15)(@latticexyz/world@2.0.0-next.15)(@types/react@18.2.22)(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-BPzxOaNb5ia/5IksfgO96XiJo+cvJYbL77haWFkVMIJtyb+ToWWtHm8vjHOor3OeicpffXEPEJUKIdbwKHaAwg==} + peerDependencies: + '@latticexyz/common': 2.0.0-next.15 + '@latticexyz/recs': 2.0.0-next.15 + '@latticexyz/store': 2.0.0-next.15 + '@latticexyz/store-sync': 2.0.0-next.15 + '@latticexyz/utils': 2.0.0-next.15 + '@latticexyz/world': 2.0.0-next.15 + dependencies: + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/react': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/recs': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/store': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/store-sync': 2.0.0-next.15(@types/react@18.2.22)(react@18.2.0)(typescript@5.1.6) + '@latticexyz/utils': 2.0.0-next.15 + '@latticexyz/world': 2.0.0-next.15(typescript@5.1.6) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router-dom: 6.21.1(react-dom@18.2.0)(react@18.2.0) + rxjs: 7.5.5 + tailwind-merge: 1.14.0 + use-local-storage-state: 18.3.3(react-dom@18.2.0)(react@18.2.0) + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + zustand: 4.4.7(@types/react@18.2.22)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - supports-color + - typescript + - utf-8-validate + - zod + dev: false + + /@latticexyz/gas-report@2.0.0-next.15: + resolution: {integrity: sha512-/n4EKkkr4GUjopbPnnyNtLnu8EqOUnoT1QKX+6R5BW7uu1zdkvE+WpTiPpDHgV1lP1b/qIX9+z5WGbb2nknPtw==} + hasBin: true + dependencies: + chalk: 5.3.0 + dotenv: 16.3.1 + execa: 7.2.0 + stream-to-array: 2.3.0 + strip-ansi: 7.1.0 + table: 6.8.1 + yargs: 17.7.2 + + /@latticexyz/protocol-parser@2.0.0-next.15(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-J6hMEhW2XnBGs5o1dccTO7Qbpg+waN3EdYVyp7H2c2fmHZs0LByZuzbYY4Qqx6CCsqWqS8N4CPROIWqH5vrl+Q==} + dependencies: + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + abitype: 0.9.8(typescript@5.1.6)(zod@3.22.4) + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + + /@latticexyz/react@2.0.0-next.15(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-/pCGph3j4qXsB5q9CcsT5rMJyOeF7DIzX3+rAk2YUROihS3TgQSieNCdw9Bc9KZi+X73S6T/+MEy5+Mtnat2+A==} + dependencies: + '@latticexyz/recs': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/store': 2.0.0-next.15(typescript@5.1.6) + fast-deep-equal: 3.1.3 + mobx: 6.12.0 + react: 18.2.0 + rxjs: 7.5.5 + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + dev: false + + /@latticexyz/recs@2.0.0-next.15(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-ECqWQ5t3ZkLCFgF/Nrh+S6JsbmJ8egVOmnW+WO+7LTShODU2ZCSW+cpRvFaWJQGd/BSprxBK7nExF3bD/vhKHQ==} + dependencies: + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/utils': 2.0.0-next.15 + mobx: 6.12.0 + rxjs: 7.5.5 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + dev: false + + /@latticexyz/schema-type@2.0.0-next.15(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-Tx23kYFhF10cGmapfV8/Bc/rqfOh0FWvVyEJ6oVYks7cuaSHj9Zw0my0lZy4Zi9RleVYArNnsgcOyLLBsHtaKw==} + dependencies: + abitype: 0.9.8(typescript@5.1.6)(zod@3.22.4) + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + /@latticexyz/services@2.0.0-next.15(google-protobuf@3.21.2): + resolution: {integrity: sha512-stV0B/5rNA+YvGKfzULCrqLI72vN4LzEnRRdYoL3GWrAUHX/iI9v6DWyTiLcZ9+GgJTtrFrCrmy8UtKUM4EMug==} + dependencies: + long: 5.2.3 + nice-grpc-common: 2.0.2 + nice-grpc-web: 2.0.2(google-protobuf@3.21.2) + protobufjs: 7.2.5 + transitivePeerDependencies: + - google-protobuf + + /@latticexyz/store-sync@2.0.0-next.15(@types/react@18.2.22)(react@18.2.0)(typescript@5.1.6): + resolution: {integrity: sha512-E8OpSPCLCQ41kG58PPs5SDRhYQPUZrE/fzm7FObPYfV5YGMdNRMfp9QvvSjFRoBXDoC+gQiLsX1mgLTzkHTDZQ==} + dependencies: + '@latticexyz/block-logs-stream': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/protocol-parser': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/recs': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/store': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/world': 2.0.0-next.15(typescript@5.1.6) + '@trpc/client': 10.34.0(@trpc/server@10.34.0) + '@trpc/server': 10.34.0 + change-case: 5.4.0 + debug: 4.3.4 + drizzle-orm: 0.28.6(kysely@0.26.3)(postgres@3.4.3)(sql.js@1.9.0) + kysely: 0.26.3 + postgres: 3.4.3 + rxjs: 7.5.5 + sql.js: 1.9.0 + superjson: 1.13.3 + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + zod: 3.22.4 + zustand: 4.4.7(@types/react@18.2.22)(react@18.2.0) + transitivePeerDependencies: + - '@aws-sdk/client-rds-data' + - '@cloudflare/workers-types' + - '@libsql/client' + - '@neondatabase/serverless' + - '@opentelemetry/api' + - '@planetscale/database' + - '@types/better-sqlite3' + - '@types/pg' + - '@types/react' + - '@types/sql.js' + - '@vercel/postgres' + - better-sqlite3 + - bufferutil + - bun-types + - immer + - knex + - mysql2 + - pg + - react + - sqlite3 + - supports-color + - typescript + - utf-8-validate + dev: false + + /@latticexyz/store@2.0.0-next.15(typescript@5.1.6): + resolution: {integrity: sha512-ZU5O8hT9BqXPEFgEkpBJgcJeqVumYEAILhc7SxayKtbsLsjVuxdflH67Snvx4U+EHUXiO8BflCVrbBT5b57Vmw==} + dependencies: + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/config': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + abitype: 0.9.8(typescript@5.1.6)(zod@3.22.4) + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + + /@latticexyz/utils@2.0.0-next.15: + resolution: {integrity: sha512-kal4N+VPnvxkkWdPrWKZeFwWDle7JAw9JQq54Ur6/VmH82tbvcrkeOgESIz3RFuzworFK2wyHuaxF9wq3aWxEw==} + dependencies: + mobx: 6.12.0 + proxy-deep: 3.1.1 + rxjs: 7.5.5 + + /@latticexyz/world-modules@2.0.0-next.15(typescript@5.1.6): + resolution: {integrity: sha512-cXT5nWSA2/0gFt3W5OwQhAKtd3546O2lwC1Cpbk+uGKgmsjF7fw6jriN8oVuWjba2OVIQvXqqTc8drrRjTZ4zQ==} + dependencies: + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/config': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/store': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/world': 2.0.0-next.15(typescript@5.1.6) + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + + /@latticexyz/world@2.0.0-next.15(typescript@5.1.6): + resolution: {integrity: sha512-Ypydf7sCckEqWRzkKWQleNAIenj0e5Lz5GggTJ98B3t5rsdNmBAjTJedAZpFcs1VSvwXdVanZ3+Rk7KY11vEqA==} + dependencies: + '@latticexyz/common': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/config': 2.0.0-next.15(typescript@5.1.6) + '@latticexyz/schema-type': 2.0.0-next.15(typescript@5.1.6)(zod@3.22.4) + '@latticexyz/store': 2.0.0-next.15(typescript@5.1.6) + abitype: 0.9.8(typescript@5.1.6)(zod@3.22.4) + viem: 1.14.0(typescript@5.1.6)(zod@3.22.4) + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - typescript + - utf-8-validate + + /@noble/curves@1.2.0: + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + dependencies: + '@noble/hashes': 1.3.2 + + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.16.0 + dev: true + + /@protobufjs/aspromise@1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + /@protobufjs/base64@1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + /@protobufjs/codegen@2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + /@protobufjs/eventemitter@1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + /@protobufjs/fetch@1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + /@protobufjs/float@1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + /@protobufjs/inquire@1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + /@protobufjs/path@1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + /@protobufjs/pool@1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + /@protobufjs/utf8@1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + /@remix-run/router@1.14.1: + resolution: {integrity: sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==} + engines: {node: '>=14.0.0'} + dev: false + + /@scure/base@1.1.5: + resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==} + + /@scure/bip32@1.3.2: + resolution: {integrity: sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==} + dependencies: + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.5 + + /@scure/bip39@1.2.1: + resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} + dependencies: + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.5 + + /@solidity-parser/parser@0.16.2: + resolution: {integrity: sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==} + dependencies: + antlr4ts: 0.5.0-alpha.4 + + /@trpc/client@10.34.0(@trpc/server@10.34.0): + resolution: {integrity: sha512-nqtDTIqSY/9syo2EjSy4WWWXPU9GsamEh9Tsg698gLAh1nhgFc5+/YYeb+Ne1pbvWGZ5/3t9Dcz3h4wMyyJ9gQ==} + peerDependencies: + '@trpc/server': 10.34.0 + dependencies: + '@trpc/server': 10.34.0 + dev: false + + /@trpc/server@10.34.0: + resolution: {integrity: sha512-2VMW44Fpaoyqb50dBtzdSWMhqt8lmoJiocEyBBeDb03R0W+XrzbVD5kU/wqKPlcp1DWeNCkOEIMtetMZCfo1hA==} + dev: false + + /@types/debug@4.1.7: + resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + dependencies: + '@types/ms': 0.7.34 + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/ms@0.7.34: + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + dev: true + + /@types/node@18.19.6: + resolution: {integrity: sha512-X36s5CXMrrJOs2lQCdDF68apW4Rfx9ixYMawlepwmE4Anezv/AV2LSpKD1Ub8DAc+urp5bk0BGZ6NtmBitfnsg==} + dependencies: + undici-types: 5.26.5 + + /@types/prettier@2.7.2: + resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} + dev: true + + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + + /@types/react-dom@18.2.7: + resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} + dependencies: + '@types/react': 18.2.22 + dev: true + + /@types/react@18.2.22: + resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 + + /@types/retry@0.12.1: + resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} + + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + + /@types/semver@7.5.6: + resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + dev: true + + /@types/ws@8.5.10: + resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} + dependencies: + '@types/node': 18.19.6 + + /@typescript-eslint/eslint-plugin@5.46.1(@typescript-eslint/parser@5.46.1)(eslint@8.29.0)(typescript@5.1.6): + resolution: {integrity: sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.46.1(eslint@8.29.0)(typescript@5.1.6) + '@typescript-eslint/scope-manager': 5.46.1 + '@typescript-eslint/type-utils': 5.46.1(eslint@8.29.0)(typescript@5.1.6) + '@typescript-eslint/utils': 5.46.1(eslint@8.29.0)(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.29.0 + ignore: 5.3.0 + natural-compare-lite: 1.4.0 + regexpp: 3.2.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.46.1(eslint@8.29.0)(typescript@5.1.6): + resolution: {integrity: sha512-RelQ5cGypPh4ySAtfIMBzBGyrNerQcmfA1oJvPj5f+H4jI59rl9xxpn4bonC0tQvUKOEN7eGBFWxFLK3Xepneg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.46.1 + '@typescript-eslint/types': 5.46.1 + '@typescript-eslint/typescript-estree': 5.46.1(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.29.0 + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.46.1: + resolution: {integrity: sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.46.1 + '@typescript-eslint/visitor-keys': 5.46.1 + dev: true + + /@typescript-eslint/type-utils@5.46.1(eslint@8.29.0)(typescript@5.1.6): + resolution: {integrity: sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.46.1(typescript@5.1.6) + '@typescript-eslint/utils': 5.46.1(eslint@8.29.0)(typescript@5.1.6) + debug: 4.3.4 + eslint: 8.29.0 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.46.1: + resolution: {integrity: sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.46.1(typescript@5.1.6): + resolution: {integrity: sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.46.1 + '@typescript-eslint/visitor-keys': 5.46.1 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.46.1(eslint@8.29.0)(typescript@5.1.6): + resolution: {integrity: sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.6 + '@typescript-eslint/scope-manager': 5.46.1 + '@typescript-eslint/types': 5.46.1 + '@typescript-eslint/typescript-estree': 5.46.1(typescript@5.1.6) + eslint: 8.29.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0(eslint@8.29.0) + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.46.1: + resolution: {integrity: sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.46.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /@vitejs/plugin-react@3.1.0(vite@4.5.1): + resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.1.0-beta.0 + dependencies: + '@babel/core': 7.23.7 + '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.7) + magic-string: 0.27.0 + react-refresh: 0.14.0 + vite: 4.5.1 + transitivePeerDependencies: + - supports-color + dev: true + + /abitype@0.9.8(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + dependencies: + typescript: 5.1.6 + zod: 3.22.4 + + /abort-controller-x@0.4.3: + resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==} + + /acorn-jsx@5.3.2(acorn@8.11.3): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.3 + dev: true + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /aes-js@3.0.0: + resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + + /antlr4@4.13.1: + resolution: {integrity: sha512-kiXTspaRYvnIArgE97z5YVVf/cDVQABr3abFRR6mE7yesLMkgu4ujuyV/sgxafQ8wgve0DJQUJ38Z8tkgA2izA==} + engines: {node: '>=16'} + dev: true + + /antlr4ts@0.5.0-alpha.4: + resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==} + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.5 + is-array-buffer: 3.0.2 + dev: true + + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.tosorted@1.1.2: + resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + get-intrinsic: 1.2.2 + dev: true + + /arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + + /ast-parents@0.0.1: + resolution: {integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==} + dev: true + + /astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + /bech32@1.1.4: + resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + + /bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + + /bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + + /brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + /browser-headers@0.4.1: + resolution: {integrity: sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==} + + /browserslist@4.22.2: + resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001576 + electron-to-chromium: 1.4.626 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.22.2) + dev: true + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /caniuse-lite@1.0.30001576: + resolution: {integrity: sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + /change-case@5.4.0: + resolution: {integrity: sha512-11YRFf0f4pI+ROHUfq64WivyrcNSrZjdDt2qgVxvAObtj/Pwnu5uAKObHRbN9uAhaDFkvkqcKVEl8Dxnmx+N7w==} + dev: false + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + + /commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + dependencies: + is-what: 4.1.16 + dev: false + + /cosmiconfig@8.3.6(typescript@5.1.6): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.1.6 + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + has-property-descriptors: 1.0.1 + object-keys: 1.1.1 + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + + /drizzle-orm@0.28.6(kysely@0.26.3)(postgres@3.4.3)(sql.js@1.9.0): + resolution: {integrity: sha512-yBe+F9htrlYER7uXgDJUQsTHFoIrI5yMm5A0bg0GiZ/kY5jNXTWoEy4KQtg35cE27sw1VbgzoMWHAgCckUUUww==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=3' + '@libsql/client': '*' + '@neondatabase/serverless': '>=0.1' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@vercel/postgres': '*' + better-sqlite3: '>=7' + bun-types: '*' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@libsql/client': + optional: true + '@neondatabase/serverless': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@vercel/postgres': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + dependencies: + kysely: 0.26.3 + postgres: 3.4.3 + sql.js: 1.9.0 + dev: false + + /ejs@3.1.9: + resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.8.7 + + /electron-to-chromium@1.4.626: + resolution: {integrity: sha512-f7/be56VjRRQk+Ric6PmIrEtPcIqsn3tElyAu9Sh6egha2VLJ82qwkcOdcnT06W+Pb6RUulV1ckzrGbKzVcTHg==} + dev: true + + /elliptic@6.5.4: + resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract@1.22.3: + resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.2 + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + es-set-tostringtag: 2.0.2 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.2 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + internal-slot: 1.0.6 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.1 + safe-array-concat: 1.0.1 + safe-regex-test: 1.0.1 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.13 + dev: true + + /es-set-tostringtag@2.0.2: + resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + has-tostringtag: 1.0.0 + hasown: 2.0.0 + dev: true + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.0 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.29.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.29.0 + dev: true + + /eslint-plugin-react@7.31.11(eslint@8.29.0): + resolution: {integrity: sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.2 + doctrine: 2.1.0 + eslint: 8.29.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.10 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils@3.0.0(eslint@8.29.0): + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.29.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.29.0: + resolution: {integrity: sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.4.1 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-utils: 3.0.0(eslint@8.29.0) + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + grapheme-splitter: 1.0.4 + ignore: 5.3.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.4.2 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /ethers@5.7.2: + resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/contracts': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/json-wallets': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/solidity': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/units': 5.7.0 + '@ethersproject/wallet': 5.7.0 + '@ethersproject/web': 5.7.1 + '@ethersproject/wordlists': 5.7.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + /eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.2.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.16.0: + resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + dependencies: + minimatch: 5.1.6 + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /google-protobuf@3.21.2: + resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==} + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + dev: true + + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: true + + /hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + + /ignore@5.3.0: + resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + /inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /internal-slot@1.0.6: + resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + hasown: 2.0.0 + side-channel: 1.0.4 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.13 + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + dev: false + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + /isomorphic-ws@5.0.0(ws@8.13.0): + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + dependencies: + ws: 8.13.0 + + /jake@10.8.7: + resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} + engines: {node: '>=10'} + hasBin: true + dependencies: + async: 3.2.5 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + /js-base64@3.7.5: + resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} + + /js-sdsl@4.4.2: + resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==} + dev: true + + /js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 + object.assign: 4.1.5 + object.values: 1.1.7 + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kysely@0.26.3: + resolution: {integrity: sha512-yWSgGi9bY13b/W06DD2OCDDHQmq1kwTGYlQ4wpZkMOJqMGCstVCFIvxCCVG4KfY1/3G0MhDAcZsip/Lw8/vJWw==} + engines: {node: '>=14.0.0'} + dev: false + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-locate: 6.0.0 + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + + /magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + /minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + /minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + + /mobx@6.12.0: + resolution: {integrity: sha512-Mn6CN6meXEnMa0a5u6a5+RKrqRedHBhZGd15AWLk9O6uFY4KYHzImdt8JI8WODo1bjTSRnwXhJox+FCUZhCKCQ==} + + /mprocs@0.6.4: + resolution: {integrity: sha512-Y4eqnAjp3mjy0eT+zPoMQ+P/ISOzjgRG/4kh4I5cRA4Tv0rPxTCBRadn3+j+boMF5id7IoLhrVq9NFWFPuzD9A==} + engines: {node: '>=0.10.0'} + hasBin: true + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /nice-grpc-common@2.0.2: + resolution: {integrity: sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==} + dependencies: + ts-error: 1.0.6 + + /nice-grpc-web@2.0.2(google-protobuf@3.21.2): + resolution: {integrity: sha512-hIgvd0ufjOR1FqrakqQ1HdO6A8M1rps9cTCnO+8aPml2NXyLOsuZCdzCp/f31Y1ngBGvDkoalW5H+CBd11x+GQ==} + dependencies: + '@improbable-eng/grpc-web': 0.15.0(google-protobuf@3.21.2) + abort-controller-x: 0.4.3 + js-base64: 3.7.5 + nice-grpc-common: 2.0.2 + transitivePeerDependencies: + - google-protobuf + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + /npm-run-path@5.2.0: + resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + + /openurl@1.1.1: + resolution: {integrity: sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==} + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-limit: 4.0.0 + + /p-queue@7.4.1: + resolution: {integrity: sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA==} + engines: {node: '>=12'} + dependencies: + eventemitter3: 5.0.1 + p-timeout: 5.1.0 + + /p-retry@5.1.2: + resolution: {integrity: sha512-couX95waDu98NfNZV+i/iLt+fdVxmI7CbrrdC2uDWfPdUAApyxT4wmDlyOtR5KtTDmkDO0zDScDjDou9YHhd9g==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + '@types/retry': 0.12.1 + retry: 0.13.1 + + /p-timeout@5.1.0: + resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} + engines: {node: '>=12'} + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.23.5 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /path@0.12.7: + resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + dependencies: + process: 0.11.10 + util: 0.10.4 + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /postcss@8.4.33: + resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /postgres@3.4.3: + resolution: {integrity: sha512-iHJn4+M9vbTdHSdDzNkC0crHq+1CUdFhx+YqCE+SqWxPjm+Zu63jq7yZborOBF64c8pc58O5uMudyL1FQcHacA==} + engines: {node: '>=12'} + dev: false + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-plugin-solidity@1.1.3(prettier@2.8.8): + resolution: {integrity: sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==} + engines: {node: '>=12'} + peerDependencies: + prettier: '>=2.3.0 || >=3.0.0-alpha.0' + dependencies: + '@solidity-parser/parser': 0.16.2 + prettier: 2.8.8 + semver: 7.5.4 + solidity-comments-extractor: 0.0.7 + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + + /protobufjs@7.2.5: + resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==} + engines: {node: '>=12.0.0'} + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 18.19.6 + long: 5.2.3 + + /proxy-deep@3.1.1: + resolution: {integrity: sha512-kppbvLUNJ4IOMZds9/4gz/rtT5OFiesy3XosLsgMKlF3vb6GA5Y3ptyDlzKLcOcUBW+zaY+RiMINTsgE+O6e+Q==} + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + + /react-refresh@0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react-router-dom@6.21.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + '@remix-run/router': 1.14.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router: 6.21.1(react@18.2.0) + dev: false + + /react-router@6.21.1(react@18.2.0): + resolution: {integrity: sha512-W0l13YlMTm1YrpVIOpjCADJqEUpz1vm+CMo47RuFX4Ftegwm6KOYsL5G3eiE52jnJpKvzm6uB/vTKTPKM8dmkA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + dependencies: + '@remix-run/router': 1.14.1 + react: 18.2.0 + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + + /regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + set-function-name: 2.0.1 + dev: true + + /regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs@7.5.5: + resolution: {integrity: sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==} + dependencies: + tslib: 2.6.2 + + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-regex-test@1.0.1: + resolution: {integrity: sha512-Y5NejJTTliTyY4H7sipGqY+RX5P87i3F7c4Rcepy72nq+mNLhIsD0W4c7kEmduMDQCSqtPsXPlSTsFhh2LQv+g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-regex: 1.1.4 + dev: true + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /scrypt-js@3.0.1: + resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.1 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + /solhint-config-mud@2.0.0-next.15: + resolution: {integrity: sha512-QvNcDFBNXcW63jqDN9oVUSn68B/UhSbaqiwn9S40jrGKpr7m7hIpJvsB/IzvcLAjryFWANVPWzc36iSNAOn2KA==} + dev: true + + /solhint-plugin-mud@2.0.0-next.15: + resolution: {integrity: sha512-3EZ1sc3x8sr/A64r2Sa1bx+l0Lec308l0gZQ3qYJTN8GpG3bjG3fr6SugBjWvyPjZm00SvfJbDcP/y+u2ewb2Q==} + dependencies: + '@solidity-parser/parser': 0.16.2 + dev: true + + /solhint@3.6.2(typescript@5.1.6): + resolution: {integrity: sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ==} + hasBin: true + dependencies: + '@solidity-parser/parser': 0.16.2 + ajv: 6.12.6 + antlr4: 4.13.1 + ast-parents: 0.0.1 + chalk: 4.1.2 + commander: 10.0.1 + cosmiconfig: 8.3.6(typescript@5.1.6) + fast-diff: 1.3.0 + glob: 8.1.0 + ignore: 5.3.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + pluralize: 8.0.0 + semver: 7.5.4 + strip-ansi: 6.0.1 + table: 6.8.1 + text-table: 0.2.0 + optionalDependencies: + prettier: 2.8.8 + transitivePeerDependencies: + - typescript + dev: true + + /solidity-comments-extractor@0.0.7: + resolution: {integrity: sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==} + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /sql.js@1.9.0: + resolution: {integrity: sha512-+QMN8NU5KJxofT+lEaSLYdhh+Pdq7ZYS6X5bSbpmD+4SKFf+qBmr+coKT07LZ+keUNh1sf3Nz9dQwD8WNI2i/Q==} + dev: false + + /stream-to-array@2.3.0: + resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} + dependencies: + any-promise: 1.3.0 + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + regexp.prototype.flags: 1.5.1 + set-function-name: 2.0.1 + side-channel: 1.0.4 + dev: true + + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /superjson@1.13.3: + resolution: {integrity: sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==} + engines: {node: '>=10'} + dependencies: + copy-anything: 3.0.5 + dev: false + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /table@6.8.1: + resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} + engines: {node: '>=10.0.0'} + dependencies: + ajv: 8.12.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + /tailwind-merge@1.14.0: + resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==} + dev: false + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /throttle-debounce@5.0.0: + resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==} + engines: {node: '>=12.22'} + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + + /ts-error@1.0.6: + resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==} + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + /tsutils@3.21.0(typescript@5.1.6): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.1.6 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: true + + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} + hasBin: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.5 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + /update-browserslist-db@1.0.13(browserslist@4.22.2): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.2 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + + /use-local-storage-state@18.3.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-SwwW6LPbxf3q5XimJyYE2jBefpvEJTjAgBO47wCs0+ZkL/Hx8heF/0wtBJ7Df0SiSwyfNDIPHo+8Z3q569jlow==} + engines: {node: '>=12'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + dependencies: + inherits: 2.0.3 + + /viem@1.14.0(typescript@5.1.6)(zod@3.22.4): + resolution: {integrity: sha512-4d+4/H3lnbkSAbrpQ15i1nBA7hne06joLFy3L3m0ZpMc+g+Zr3D4nuSTyeiqbHAYs9m2P9Kjap0HlyGkehasgg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@adraffy/ens-normalize': 1.9.4 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/bip32': 1.3.2 + '@scure/bip39': 1.2.1 + '@types/ws': 8.5.10 + abitype: 0.9.8(typescript@5.1.6)(zod@3.22.4) + isomorphic-ws: 5.0.0(ws@8.13.0) + typescript: 5.1.6 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + /vite@4.5.1: + resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.18.20 + postcss: 8.4.33 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /wait-port@1.1.0: + resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chalk: 4.1.2 + commander: 9.5.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-typed-array@1.1.13: + resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + /ws@7.4.6: + resolution: {integrity: sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + /ws@8.13.0: + resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + + /zod-validation-error@1.5.0(zod@3.22.4): + resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} + engines: {node: '>=16.0.0'} + peerDependencies: + zod: ^3.18.0 + dependencies: + zod: 3.22.4 + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + + /zustand@4.4.7(@types/react@18.2.22)(react@18.2.0): + resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + dependencies: + '@types/react': 18.2.22 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + github.com/dapphub/ds-test/e282159d5170298eb2455a6c05280ab5a73a4ef0: + resolution: {tarball: https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0} + name: ds-test + version: 1.0.0 + dev: true + + github.com/foundry-rs/forge-std/74cfb77e308dd188d2f58864aaf44963ae6b88b1: + resolution: {tarball: https://codeload.github.com/foundry-rs/forge-std/tar.gz/74cfb77e308dd188d2f58864aaf44963ae6b88b1} + name: forge-std + version: 1.6.0 + dev: true diff --git a/examples/multiple-accounts/pnpm-workspace.yaml b/examples/multiple-accounts/pnpm-workspace.yaml new file mode 100644 index 0000000000..924b55f42e --- /dev/null +++ b/examples/multiple-accounts/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - packages/* diff --git a/packages/cli/contracts/src/codegen/tables/Dynamics1.sol b/packages/cli/contracts/src/codegen/tables/Dynamics1.sol index 0c656c4b95..a749ee0b67 100644 --- a/packages/cli/contracts/src/codegen/tables/Dynamics1.sol +++ b/packages/cli/contracts/src/codegen/tables/Dynamics1.sol @@ -1176,7 +1176,7 @@ library Dynamics1 { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( bytes32[1] memory staticB32, diff --git a/packages/cli/contracts/src/codegen/tables/Dynamics2.sol b/packages/cli/contracts/src/codegen/tables/Dynamics2.sol index f697ba36d3..8fa1b6c9de 100644 --- a/packages/cli/contracts/src/codegen/tables/Dynamics2.sol +++ b/packages/cli/contracts/src/codegen/tables/Dynamics2.sol @@ -762,7 +762,7 @@ library Dynamics2 { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( uint64[] memory u64, diff --git a/packages/cli/contracts/src/codegen/tables/Offchain.sol b/packages/cli/contracts/src/codegen/tables/Offchain.sol index 19d095c784..d110880813 100644 --- a/packages/cli/contracts/src/codegen/tables/Offchain.sol +++ b/packages/cli/contracts/src/codegen/tables/Offchain.sol @@ -191,7 +191,7 @@ library Offchain { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/packages/cli/contracts/src/codegen/tables/Singleton.sol b/packages/cli/contracts/src/codegen/tables/Singleton.sol index ea0ee6dcca..430df832f9 100644 --- a/packages/cli/contracts/src/codegen/tables/Singleton.sol +++ b/packages/cli/contracts/src/codegen/tables/Singleton.sol @@ -746,7 +746,7 @@ library Singleton { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( int256 v1, diff --git a/packages/cli/contracts/src/codegen/tables/Statics.sol b/packages/cli/contracts/src/codegen/tables/Statics.sol index d906ebeeda..80d3d8a95d 100644 --- a/packages/cli/contracts/src/codegen/tables/Statics.sol +++ b/packages/cli/contracts/src/codegen/tables/Statics.sol @@ -738,7 +738,7 @@ library Statics { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( uint256 v1, diff --git a/packages/cli/contracts/src/codegen/tables/UserTyped.sol b/packages/cli/contracts/src/codegen/tables/UserTyped.sol index 24a11dbb75..22c230bbdc 100644 --- a/packages/cli/contracts/src/codegen/tables/UserTyped.sol +++ b/packages/cli/contracts/src/codegen/tables/UserTyped.sol @@ -825,7 +825,7 @@ library UserTyped { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( TestTypeAddress v1, diff --git a/packages/cli/src/deploy/assertNamespaceOwner.ts b/packages/cli/src/deploy/assertNamespaceOwner.ts deleted file mode 100644 index 4ed1e15c81..0000000000 --- a/packages/cli/src/deploy/assertNamespaceOwner.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Account, Chain, Client, Hex, Transport, getAddress } from "viem"; -import { WorldDeploy, worldTables } from "./common"; -import { hexToResource, resourceToHex } from "@latticexyz/common"; -import { getResourceIds } from "./getResourceIds"; -import { getTableValue } from "./getTableValue"; - -export async function assertNamespaceOwner({ - client, - worldDeploy, - resourceIds, -}: { - readonly client: Client; - readonly worldDeploy: WorldDeploy; - readonly resourceIds: readonly Hex[]; -}): Promise { - const desiredNamespaces = Array.from(new Set(resourceIds.map((resourceId) => hexToResource(resourceId).namespace))); - const existingResourceIds = await getResourceIds({ client, worldDeploy }); - const existingNamespaces = Array.from( - new Set(existingResourceIds.map((resourceId) => hexToResource(resourceId).namespace)) - ); - - const namespaces = desiredNamespaces.filter((namespace) => existingNamespaces.includes(namespace)); - const namespaceOwners = await Promise.all( - namespaces.map(async (namespace) => { - const { owner } = await getTableValue({ - client, - worldDeploy, - table: worldTables.world_NamespaceOwner, - key: { namespaceId: resourceToHex({ type: "namespace", namespace, name: "" }) }, - }); - return [namespace, owner]; - }) - ); - - const unauthorizedNamespaces = namespaceOwners - .filter(([, owner]) => getAddress(owner) !== getAddress(client.account.address)) - .map(([namespace]) => namespace); - - if (unauthorizedNamespaces.length) { - throw new Error(`You are attempting to deploy to namespaces you do not own: ${unauthorizedNamespaces.join(", ")}`); - } -} diff --git a/packages/cli/src/deploy/deploy.ts b/packages/cli/src/deploy/deploy.ts index b75c189ed7..0973392c9b 100644 --- a/packages/cli/src/deploy/deploy.ts +++ b/packages/cli/src/deploy/deploy.ts @@ -9,12 +9,12 @@ import { getWorldDeploy } from "./getWorldDeploy"; import { ensureFunctions } from "./ensureFunctions"; import { ensureModules } from "./ensureModules"; import { Table } from "./configToTables"; -import { assertNamespaceOwner } from "./assertNamespaceOwner"; +import { ensureNamespaceOwner } from "./ensureNamespaceOwner"; import { debug } from "./debug"; import { resourceLabel } from "./resourceLabel"; import { uniqueBy } from "@latticexyz/common/utils"; import { ensureContractsDeployed } from "./ensureContractsDeployed"; -import { coreModuleBytecode, worldFactoryBytecode, worldFactoryContracts } from "./ensureWorldFactory"; +import { worldFactoryContracts } from "./ensureWorldFactory"; type DeployOptions = { client: Client; @@ -67,12 +67,17 @@ export async function deploy({ throw new Error(`Unsupported World version: ${worldDeploy.worldVersion}`); } - await assertNamespaceOwner({ + const namespaceTxs = await ensureNamespaceOwner({ client, worldDeploy, resourceIds: [...tables.map((table) => table.tableId), ...systems.map((system) => system.systemId)], }); + debug("waiting for all namespace registration transactions to confirm"); + for (const tx of namespaceTxs) { + await waitForTransactionReceipt(client, { hash: tx }); + } + const tableTxs = await ensureTables({ client, worldDeploy, diff --git a/packages/cli/src/deploy/ensureNamespaceOwner.ts b/packages/cli/src/deploy/ensureNamespaceOwner.ts new file mode 100644 index 0000000000..65384d2de3 --- /dev/null +++ b/packages/cli/src/deploy/ensureNamespaceOwner.ts @@ -0,0 +1,71 @@ +import { Account, Chain, Client, Hex, Transport, getAddress } from "viem"; +import { WorldDeploy, worldAbi, worldTables } from "./common"; +import { hexToResource, resourceToHex, writeContract } from "@latticexyz/common"; +import { getResourceIds } from "./getResourceIds"; +import { getTableValue } from "./getTableValue"; +import { debug } from "./debug"; + +export async function ensureNamespaceOwner({ + client, + worldDeploy, + resourceIds, +}: { + readonly client: Client; + readonly worldDeploy: WorldDeploy; + readonly resourceIds: readonly Hex[]; +}): Promise { + const desiredNamespaces = Array.from(new Set(resourceIds.map((resourceId) => hexToResource(resourceId).namespace))); + const existingResourceIds = await getResourceIds({ client, worldDeploy }); + const existingNamespaces = new Set(existingResourceIds.map((resourceId) => hexToResource(resourceId).namespace)); + if (existingNamespaces.size) { + debug( + "found", + existingNamespaces.size, + "existing namespaces:", + Array.from(existingNamespaces) + .map((namespace) => (namespace === "" ? "" : namespace)) + .join(", ") + ); + } + + // Assert ownership of existing namespaces + const existingDesiredNamespaces = desiredNamespaces.filter((namespace) => existingNamespaces.has(namespace)); + const namespaceOwners = await Promise.all( + existingDesiredNamespaces.map(async (namespace) => { + const { owner } = await getTableValue({ + client, + worldDeploy, + table: worldTables.world_NamespaceOwner, + key: { namespaceId: resourceToHex({ type: "namespace", namespace, name: "" }) }, + }); + return [namespace, owner]; + }) + ); + + const unauthorizedNamespaces = namespaceOwners + .filter(([, owner]) => getAddress(owner) !== getAddress(client.account.address)) + .map(([namespace]) => namespace); + + if (unauthorizedNamespaces.length) { + throw new Error(`You are attempting to deploy to namespaces you do not own: ${unauthorizedNamespaces.join(", ")}`); + } + + // Register missing namespaces + const missingNamespaces = desiredNamespaces.filter((namespace) => !existingNamespaces.has(namespace)); + if (missingNamespaces.length > 0) { + debug("registering namespaces", Array.from(missingNamespaces).join(", ")); + } + const registrationTxs = Promise.all( + missingNamespaces.map((namespace) => + writeContract(client, { + chain: client.chain ?? null, + address: worldDeploy.address, + abi: worldAbi, + functionName: "registerNamespace", + args: [resourceToHex({ namespace, type: "namespace", name: "" })], + }) + ) + ); + + return registrationTxs; +} diff --git a/packages/common/src/codegen/render-solidity/common.ts b/packages/common/src/codegen/render-solidity/common.ts index 9ce0ec120e..96041373fb 100644 --- a/packages/common/src/codegen/render-solidity/common.ts +++ b/packages/common/src/codegen/render-solidity/common.ts @@ -36,17 +36,12 @@ export function renderCommonData({ staticResourceData?: StaticResourceData; keyTuple: RenderKeyTuple[]; }): { - _tableId: string; _typedTableId: string; - _keyArgs: string; _typedKeyArgs: string; _keyTupleDefinition: string; } { // static resource means static tableId as well, and no tableId arguments - const _tableId = staticResourceData ? "" : "_tableId"; const _typedTableId = staticResourceData ? "" : "ResourceId _tableId"; - - const _keyArgs = renderArguments(keyTuple.map(({ name }) => name)); const _typedKeyArgs = renderArguments(keyTuple.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`)); const _keyTupleDefinition = ` @@ -55,9 +50,7 @@ export function renderCommonData({ `; return { - _tableId, _typedTableId, - _keyArgs, _typedKeyArgs, _keyTupleDefinition, }; @@ -127,21 +120,33 @@ export function renderAbsoluteImports(imports: AbsoluteImportDatum[]): string { export function renderWithStore( storeArgument: boolean, - callback: ( - _typedStore: string | undefined, - _store: string, - _commentSuffix: string, - _untypedStore: string | undefined, - _methodPrefix: string, - _internal?: boolean - ) => string + callback: (data: { + _typedStore: string | undefined; + _store: string; + _commentSuffix: string; + _methodNamePrefix: string; + _useExplicitFieldLayout?: boolean; + }) => string ): string { let result = ""; - result += callback(undefined, "StoreSwitch", "", undefined, ""); - result += callback(undefined, "StoreCore", "", undefined, "_", true); + result += callback({ _typedStore: undefined, _store: "StoreSwitch", _commentSuffix: "", _methodNamePrefix: "" }); + result += callback({ + _typedStore: undefined, + _store: "StoreCore", + _commentSuffix: "", + _methodNamePrefix: "_", + _useExplicitFieldLayout: true, + }); if (storeArgument) { - result += "\n" + callback("IStore _store", "_store", " (using the specified store)", "_store", ""); + result += + "\n" + + callback({ + _typedStore: "IStore _store", + _store: "_store", + _commentSuffix: " (using the specified store)", + _methodNamePrefix: "", + }); } return result; @@ -194,11 +199,11 @@ export function renderValueTypeToBytes32(name: string, { typeUnwrap, internalTyp if (internalTypeId === "bytes32") { return innerText; - } else if (internalTypeId.match(/^bytes\d{1,2}$/)) { + } else if (/^bytes\d{1,2}$/.test(internalTypeId)) { return `bytes32(${innerText})`; - } else if (internalTypeId.match(/^uint\d{1,3}$/)) { + } else if (/^uint\d{1,3}$/.test(internalTypeId)) { return `bytes32(uint256(${innerText}))`; - } else if (internalTypeId.match(/^int\d{1,3}$/)) { + } else if (/^int\d{1,3}$/.test(internalTypeId)) { return `bytes32(uint256(int256(${innerText})))`; } else if (internalTypeId === "address") { return `bytes32(uint256(uint160(${innerText})))`; @@ -210,7 +215,7 @@ export function renderValueTypeToBytes32(name: string, { typeUnwrap, internalTyp } export function isLeftAligned(field: Pick): boolean { - return field.internalTypeId.match(/^bytes\d{1,2}$/) !== null; + return /^bytes\d{1,2}$/.test(field.internalTypeId); } export function getLeftPaddingBits(field: Pick): number { diff --git a/packages/common/src/codegen/utils/loadUserTypesFile.ts b/packages/common/src/codegen/utils/loadUserTypesFile.ts index fe69596b44..edceddf23a 100644 --- a/packages/common/src/codegen/utils/loadUserTypesFile.ts +++ b/packages/common/src/codegen/utils/loadUserTypesFile.ts @@ -35,7 +35,7 @@ export function loadAndExtractUserTypes( } } - extractedUserTypes = Object.assign(extractedUserTypes, userTypesInFile); + extractedUserTypes = { ...extractedUserTypes, ...userTypesInFile }; } return extractedUserTypes; } diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index 746c11c6f8..11b8b8afb7 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -15,7 +15,7 @@ "file": "test/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: set field", - "gasUsed": 56241 + "gasUsed": 56271 }, { "file": "test/Callbacks.t.sol", @@ -27,7 +27,7 @@ "file": "test/Callbacks.t.sol", "test": "testSetAndGet", "name": "Callbacks: push 1 element", - "gasUsed": 32499 + "gasUsed": 32530 }, { "file": "test/FieldLayout.t.sol", @@ -159,37 +159,37 @@ "file": "test/Gas.t.sol", "test": "testCompareStorageWriteMUD", "name": "MUD storage write (cold, 1 word)", - "gasUsed": 22339 + "gasUsed": 22354 }, { "file": "test/Gas.t.sol", "test": "testCompareStorageWriteMUD", "name": "MUD storage write (cold, 1 word, partial)", - "gasUsed": 22406 + "gasUsed": 22421 }, { "file": "test/Gas.t.sol", "test": "testCompareStorageWriteMUD", "name": "MUD storage write (cold, 10 words)", - "gasUsed": 199795 + "gasUsed": 199810 }, { "file": "test/Gas.t.sol", "test": "testCompareStorageWriteMUD", "name": "MUD storage write (warm, 1 word)", - "gasUsed": 339 + "gasUsed": 354 }, { "file": "test/Gas.t.sol", "test": "testCompareStorageWriteMUD", "name": "MUD storage write (warm, 1 word, partial)", - "gasUsed": 506 + "gasUsed": 521 }, { "file": "test/Gas.t.sol", "test": "testCompareStorageWriteMUD", "name": "MUD storage write (warm, 10 words)", - "gasUsed": 1795 + "gasUsed": 1810 }, { "file": "test/Gas.t.sol", @@ -255,7 +255,7 @@ "file": "test/GasStorageLoad.t.sol", "test": "testCompareStorageLoadMUD", "name": "MUD storage load field (warm, 1 word)", - "gasUsed": 245 + "gasUsed": 2300 }, { "file": "test/GasStorageLoad.t.sol", @@ -267,7 +267,7 @@ "file": "test/GasStorageLoad.t.sol", "test": "testCompareStorageLoadMUD", "name": "MUD storage load field (warm, 1 word, partial)", - "gasUsed": 378 + "gasUsed": 300 }, { "file": "test/GasStorageLoad.t.sol", @@ -321,7 +321,7 @@ "file": "test/KeyEncoding.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register KeyEncoding table", - "gasUsed": 719017 + "gasUsed": 719062 }, { "file": "test/Mixed.t.sol", @@ -333,19 +333,19 @@ "file": "test/Mixed.t.sol", "test": "testDeleteExternalCold", "name": "delete record from Mixed (external, cold)", - "gasUsed": 24357 + "gasUsed": 24372 }, { "file": "test/Mixed.t.sol", "test": "testDeleteInternalCold", "name": "delete record from Mixed (internal, cold)", - "gasUsed": 19153 + "gasUsed": 19168 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteExternal", "name": "set record in Mixed (external, cold)", - "gasUsed": 108527 + "gasUsed": 108528 }, { "file": "test/Mixed.t.sol", @@ -357,13 +357,13 @@ "file": "test/Mixed.t.sol", "test": "testSetGetDeleteExternal", "name": "delete record from Mixed (external, warm)", - "gasUsed": 8673 + "gasUsed": 8688 }, { "file": "test/Mixed.t.sol", "test": "testSetGetDeleteInternal", "name": "set record in Mixed (internal, cold)", - "gasUsed": 103281 + "gasUsed": 103282 }, { "file": "test/Mixed.t.sol", @@ -375,7 +375,7 @@ "file": "test/Mixed.t.sol", "test": "testSetGetDeleteInternal", "name": "delete record from Mixed (internal, warm)", - "gasUsed": 7466 + "gasUsed": 7481 }, { "file": "test/PackedCounter.t.sol", @@ -555,13 +555,13 @@ "file": "test/Storage.t.sol", "test": "testStoreLoad", "name": "store 1 storage slot", - "gasUsed": 22339 + "gasUsed": 22354 }, { "file": "test/Storage.t.sol", "test": "testStoreLoad", "name": "store 34 bytes over 3 storage slots (with offset and safeTrail))", - "gasUsed": 23013 + "gasUsed": 23028 }, { "file": "test/Storage.t.sol", @@ -621,25 +621,25 @@ "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (cold, 1 slot, 1 uint32 item)", - "gasUsed": 18017 + "gasUsed": 18041 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromSecondField", "name": "pop from field (warm, 1 slot, 1 uint32 item)", - "gasUsed": 12024 + "gasUsed": 12049 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (cold, 2 slots, 10 uint32 items)", - "gasUsed": 15785 + "gasUsed": 15810 }, { "file": "test/StoreCoreDynamic.t.sol", "test": "testPopFromThirdField", "name": "pop from field (warm, 2 slots, 10 uint32 items)", - "gasUsed": 11792 + "gasUsed": 11818 }, { "file": "test/StoreCoreGas.t.sol", @@ -669,7 +669,13 @@ "file": "test/StoreCoreGas.t.sol", "test": "testDeleteData", "name": "delete record (complex data, 3 slots)", - "gasUsed": 8000 + "gasUsed": 8015 + }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testDeleteDataOffchainTable", + "name": "StoreCore: delete record in offchain table", + "gasUsed": 4687 }, { "file": "test/StoreCoreGas.t.sol", @@ -699,67 +705,67 @@ "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "register subscriber", - "gasUsed": 57939 + "gasUsed": 57954 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set record on table with subscriber", - "gasUsed": 72296 + "gasUsed": 72385 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "set static field on table with subscriber", - "gasUsed": 19761 + "gasUsed": 19837 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooks", "name": "delete record on table with subscriber", - "gasUsed": 18587 + "gasUsed": 18595 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "register subscriber", - "gasUsed": 57939 + "gasUsed": 57954 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) record on table with subscriber", - "gasUsed": 165451 + "gasUsed": 165525 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "set (dynamic) field on table with subscriber", - "gasUsed": 24439 + "gasUsed": 24446 }, { "file": "test/StoreCoreGas.t.sol", "test": "testHooksDynamicData", "name": "delete (dynamic) record on table with subscriber", - "gasUsed": 20253 + "gasUsed": 20261 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToDynamicField", "name": "push to field (1 slot, 1 uint32 item)", - "gasUsed": 9444 + "gasUsed": 9475 }, { "file": "test/StoreCoreGas.t.sol", "test": "testPushToDynamicField", "name": "push to field (2 slots, 10 uint32 items)", - "gasUsed": 32114 + "gasUsed": 32150 }, { "file": "test/StoreCoreGas.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "StoreCore: register table", - "gasUsed": 640899 + "gasUsed": 640944 }, { "file": "test/StoreCoreGas.t.sol", @@ -783,7 +789,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetDynamicData", "name": "set complex record with dynamic data (4 slots)", - "gasUsed": 102512 + "gasUsed": 102513 }, { "file": "test/StoreCoreGas.t.sol", @@ -825,7 +831,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (1 slot)", - "gasUsed": 31209 + "gasUsed": 31252 }, { "file": "test/StoreCoreGas.t.sol", @@ -837,7 +843,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set static field (overlap 2 slot)", - "gasUsed": 29862 + "gasUsed": 29908 }, { "file": "test/StoreCoreGas.t.sol", @@ -849,7 +855,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, first dynamic field)", - "gasUsed": 53930 + "gasUsed": 53961 }, { "file": "test/StoreCoreGas.t.sol", @@ -861,7 +867,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetField", "name": "set dynamic field (1 slot, second dynamic field)", - "gasUsed": 32153 + "gasUsed": 32186 }, { "file": "test/StoreCoreGas.t.sol", @@ -873,7 +879,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticData", "name": "set static record (1 slot)", - "gasUsed": 32756 + "gasUsed": 32782 }, { "file": "test/StoreCoreGas.t.sol", @@ -885,7 +891,7 @@ "file": "test/StoreCoreGas.t.sol", "test": "testSetAndGetStaticDataSpanningWords", "name": "set static record (2 slots)", - "gasUsed": 55260 + "gasUsed": 55289 }, { "file": "test/StoreCoreGas.t.sol", @@ -893,17 +899,23 @@ "name": "get static record (2 slots)", "gasUsed": 1740 }, + { + "file": "test/StoreCoreGas.t.sol", + "test": "testSetDataOffchainTable", + "name": "StoreCore: set record in offchain table", + "gasUsed": 8093 + }, { "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInDynamicField", "name": "update in field (1 slot, 1 uint32 item)", - "gasUsed": 8792 + "gasUsed": 8835 }, { "file": "test/StoreCoreGas.t.sol", "test": "testUpdateInDynamicField", "name": "push to field (2 slots, 6 uint64 items)", - "gasUsed": 9238 + "gasUsed": 9282 }, { "file": "test/StoreHook.t.sol", @@ -933,13 +945,13 @@ "file": "test/StoreHooks.t.sol", "test": "testOneSlot", "name": "StoreHooks: set field with one elements (cold)", - "gasUsed": 58247 + "gasUsed": 58275 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: set field (cold)", - "gasUsed": 58247 + "gasUsed": 58275 }, { "file": "test/StoreHooks.t.sol", @@ -951,55 +963,55 @@ "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: push 1 element (cold)", - "gasUsed": 12597 + "gasUsed": 12626 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: pop 1 element (warm)", - "gasUsed": 9931 + "gasUsed": 9958 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: push 1 element (warm)", - "gasUsed": 10614 + "gasUsed": 10646 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: update 1 element (warm)", - "gasUsed": 29840 + "gasUsed": 29886 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: delete record (warm)", - "gasUsed": 10410 + "gasUsed": 10425 }, { "file": "test/StoreHooks.t.sol", "test": "testTable", "name": "StoreHooks: set field (warm)", - "gasUsed": 30392 + "gasUsed": 30427 }, { "file": "test/StoreHooks.t.sol", "test": "testThreeSlots", "name": "StoreHooks: set field with three elements (cold)", - "gasUsed": 80935 + "gasUsed": 80966 }, { "file": "test/StoreHooks.t.sol", "test": "testTwoSlots", "name": "StoreHooks: set field with two elements (cold)", - "gasUsed": 80847 + "gasUsed": 80878 }, { "file": "test/StoreHooksColdLoad.t.sol", "test": "testDelete", "name": "StoreHooks: delete record (cold)", - "gasUsed": 19268 + "gasUsed": 19283 }, { "file": "test/StoreHooksColdLoad.t.sol", @@ -1023,13 +1035,13 @@ "file": "test/StoreHooksColdLoad.t.sol", "test": "testPop", "name": "StoreHooks: pop 1 element (cold)", - "gasUsed": 18366 + "gasUsed": 18390 }, { "file": "test/StoreHooksColdLoad.t.sol", "test": "testUpdate", "name": "StoreHooks: update 1 element (cold)", - "gasUsed": 20295 + "gasUsed": 20333 }, { "file": "test/StoreSwitch.t.sol", @@ -1095,13 +1107,13 @@ "file": "test/Vector2.t.sol", "test": "testRegisterAndGetFieldLayout", "name": "register Vector2 field layout", - "gasUsed": 442403 + "gasUsed": 442447 }, { "file": "test/Vector2.t.sol", "test": "testSetAndGet", "name": "set Vector2 record", - "gasUsed": 33658 + "gasUsed": 33684 }, { "file": "test/Vector2.t.sol", diff --git a/packages/store/src/FieldLayout.sol b/packages/store/src/FieldLayout.sol index 7e93bc28b1..bf88c76dd2 100644 --- a/packages/store/src/FieldLayout.sol +++ b/packages/store/src/FieldLayout.sol @@ -25,7 +25,9 @@ using FieldLayoutInstance for FieldLayout global; * various constraints regarding the length and size of the fields. */ library FieldLayoutLib { - error FieldLayoutLib_InvalidLength(uint256 length); + error FieldLayoutLib_TooManyFields(uint256 numFields, uint256 maxFields); + error FieldLayoutLib_TooManyDynamicFields(uint256 numFields, uint256 maxFields); + error FieldLayoutLib_Empty(); error FieldLayoutLib_StaticLengthIsZero(); error FieldLayoutLib_StaticLengthDoesNotFitInAWord(); @@ -41,8 +43,9 @@ library FieldLayoutLib { uint256 fieldLayout; uint256 totalLength; uint256 totalFields = _staticFields.length + numDynamicFields; - if (totalFields > MAX_TOTAL_FIELDS) revert FieldLayoutLib_InvalidLength(totalFields); - if (numDynamicFields > MAX_DYNAMIC_FIELDS) revert FieldLayoutLib_InvalidLength(numDynamicFields); + if (totalFields > MAX_TOTAL_FIELDS) revert FieldLayoutLib_TooManyFields(totalFields, MAX_TOTAL_FIELDS); + if (numDynamicFields > MAX_DYNAMIC_FIELDS) + revert FieldLayoutLib_TooManyDynamicFields(numDynamicFields, MAX_DYNAMIC_FIELDS); // Compute the total static length and store the field lengths in the encoded fieldLayout for (uint256 i = 0; i < _staticFields.length; ) { @@ -149,16 +152,18 @@ library FieldLayoutInstance { */ function validate(FieldLayout fieldLayout, bool allowEmpty) internal pure { // FieldLayout must not be empty - if (!allowEmpty && fieldLayout.isEmpty()) revert FieldLayoutLib.FieldLayoutLib_InvalidLength(0); + if (!allowEmpty && fieldLayout.isEmpty()) revert FieldLayoutLib.FieldLayoutLib_Empty(); // FieldLayout must have no more than MAX_DYNAMIC_FIELDS uint256 _numDynamicFields = fieldLayout.numDynamicFields(); - if (_numDynamicFields > MAX_DYNAMIC_FIELDS) revert FieldLayoutLib.FieldLayoutLib_InvalidLength(_numDynamicFields); + if (_numDynamicFields > MAX_DYNAMIC_FIELDS) + revert FieldLayoutLib.FieldLayoutLib_TooManyDynamicFields(_numDynamicFields, MAX_DYNAMIC_FIELDS); uint256 _numStaticFields = fieldLayout.numStaticFields(); // FieldLayout must not have more than MAX_TOTAL_FIELDS in total uint256 _numTotalFields = _numStaticFields + _numDynamicFields; - if (_numTotalFields > MAX_TOTAL_FIELDS) revert FieldLayoutLib.FieldLayoutLib_InvalidLength(_numTotalFields); + if (_numTotalFields > MAX_TOTAL_FIELDS) + revert FieldLayoutLib.FieldLayoutLib_TooManyFields(_numTotalFields, MAX_TOTAL_FIELDS); // Static lengths must be valid for (uint256 i; i < _numStaticFields; ) { diff --git a/packages/store/src/PackedCounter.sol b/packages/store/src/PackedCounter.sol index 083383ff12..eb0104cd6b 100644 --- a/packages/store/src/PackedCounter.sol +++ b/packages/store/src/PackedCounter.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; +import { BYTE_TO_BITS } from "./constants.sol"; + /** * @title PackedCounter Type Definition * @dev Describes how the packed counter is structured. @@ -18,9 +20,9 @@ using PackedCounterInstance for PackedCounter global; // Constants for packed counter handling: // Number of bits for the 7-byte accumulator -uint256 constant ACC_BITS = 7 * 8; +uint256 constant ACC_BITS = 7 * BYTE_TO_BITS; // Number of bits for the 5-byte sections -uint256 constant VAL_BITS = 5 * 8; +uint256 constant VAL_BITS = 5 * BYTE_TO_BITS; // Maximum value of a 5-byte section uint256 constant MAX_VAL = type(uint40).max; @@ -48,7 +50,7 @@ library PackedCounterLib { } /** - * @notice Packs a single value into a PackedCounter. + * @notice Packs two values into a PackedCounter. * @dev Encodes the given values 'a'-'b' into the structure of a PackedCounter. * @param a The length of the first dynamic field's data. * @param b The length of the second dynamic field's data. @@ -65,7 +67,7 @@ library PackedCounterLib { } /** - * @notice Packs a single value into a PackedCounter. + * @notice Packs three values into a PackedCounter. * @dev Encodes the given values 'a'-'c' into the structure of a PackedCounter. * @param a The length of the first dynamic field's data. * @param b The length of the second dynamic field's data. @@ -84,7 +86,7 @@ library PackedCounterLib { } /** - * @notice Packs a single value into a PackedCounter. + * @notice Packs four values into a PackedCounter. * @dev Encodes the given values 'a'-'d' into the structure of a PackedCounter. * @param a The length of the first dynamic field's data. * @param b The length of the second dynamic field's data. @@ -105,7 +107,7 @@ library PackedCounterLib { } /** - * @notice Packs a single value into a PackedCounter. + * @notice Packs five values into a PackedCounter. * @dev Encodes the given values 'a'-'e' into the structure of a PackedCounter. * @param a The length of the first dynamic field's data. * @param b The length of the second dynamic field's data. diff --git a/packages/store/src/ResourceId.sol b/packages/store/src/ResourceId.sol index 7fff4786bf..6f337dec07 100644 --- a/packages/store/src/ResourceId.sol +++ b/packages/store/src/ResourceId.sol @@ -9,7 +9,7 @@ pragma solidity >=0.8.21; type ResourceId is bytes32; /// @dev Number of bits reserved for the type in the ResourceId. -uint256 constant TYPE_BITS = 2 * 8; +uint256 constant TYPE_BITS = 2 * 8; // 2 bytes * 8 bits per byte /** * @title ResourceIdLib Library diff --git a/packages/store/src/Storage.sol b/packages/store/src/Storage.sol index b4d0d73396..a1cf0cde72 100644 --- a/packages/store/src/Storage.sol +++ b/packages/store/src/Storage.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.21; import { leftMask } from "./leftMask.sol"; import { Memory } from "./Memory.sol"; +import { BYTE_TO_BITS } from "./constants.sol"; /** * @title Storage Library @@ -27,7 +28,7 @@ library Storage { * @param data Bytes to store. */ function store(uint256 storagePointer, uint256 offset, bytes memory data) internal { - store(storagePointer, offset, Memory.dataPointer(data), data.length); + store(storagePointer, offset, data.length, Memory.dataPointer(data)); } /** @@ -37,7 +38,7 @@ library Storage { * @param memoryPointer Pointer to the start of the data in memory. * @param length Length of the data in bytes. */ - function store(uint256 storagePointer, uint256 offset, uint256 memoryPointer, uint256 length) internal { + function store(uint256 storagePointer, uint256 offset, uint256 length, uint256 memoryPointer) internal { if (offset > 0) { // Support offsets that are greater than 32 bytes by incrementing the storagePointer and decrementing the offset if (offset >= 32) { @@ -60,7 +61,7 @@ library Storage { /// @solidity memory-safe-assembly assembly { // Load data from memory and offset it to match storage - let bitOffset := mul(offset, 8) + let bitOffset := mul(offset, BYTE_TO_BITS) mask := shr(bitOffset, mask) let offsetData := shr(bitOffset, mload(memoryPointer)) @@ -121,7 +122,7 @@ library Storage { /** * @notice Set multiple storage locations to zero. * @param storagePointer The starting storage location. - * @param length The number of storage locations to set to zero. + * @param length The number of storage locations to set to zero, in bytes */ function zero(uint256 storagePointer, uint256 length) internal { // Ceil division to round up to the nearest word @@ -153,7 +154,7 @@ library Storage { * @param offset Offset within the storage location. * @return result The loaded bytes of data. */ - function load(uint256 storagePointer, uint256 length, uint256 offset) internal view returns (bytes memory result) { + function load(uint256 storagePointer, uint256 offset, uint256 length) internal view returns (bytes memory result) { uint256 memoryPointer; /// @solidity memory-safe-assembly assembly { @@ -169,7 +170,7 @@ library Storage { // Store length mstore(result, length) } - load(storagePointer, length, offset, memoryPointer); + load(storagePointer, offset, length, memoryPointer); return result; } @@ -180,7 +181,7 @@ library Storage { * @param offset Offset within the storage location. * @param memoryPointer Pointer to the location in memory to append the data. */ - function load(uint256 storagePointer, uint256 length, uint256 offset, uint256 memoryPointer) internal view { + function load(uint256 storagePointer, uint256 offset, uint256 length, uint256 memoryPointer) internal view { if (offset > 0) { // Support offsets that are greater than 32 bytes by incrementing the storagePointer and decrementing the offset if (offset >= 32) { @@ -208,14 +209,14 @@ library Storage { /// @solidity memory-safe-assembly assembly { // Load data from storage and offset it to match memory - let offsetData := shl(mul(offset, 8), sload(storagePointer)) + let offsetData := shl(mul(offset, BYTE_TO_BITS), sload(storagePointer)) mstore( memoryPointer, or( - // store the middle part + // store the left part and(offsetData, mask), - // preserve the surrounding parts + // preserve the right parts and(mload(memoryPointer), not(mask)) ) ) @@ -271,7 +272,7 @@ library Storage { * @param storagePointer The base storage location. * @param length Length of the data in bytes. * @param offset Offset within the storage location. - * @return result The loaded bytes, left-aligned bytes. Bytes beyond the length are zeroed. + * @return result The loaded bytes, left-aligned bytes. Bytes beyond the length are not zeroed. */ function loadField(uint256 storagePointer, uint256 length, uint256 offset) internal view returns (bytes32 result) { if (offset >= 32) { @@ -284,7 +285,7 @@ library Storage { // Extra data past length is not truncated // This assumes that the caller will handle the overflow bits appropriately assembly { - result := shl(mul(offset, 8), sload(storagePointer)) + result := shl(mul(offset, BYTE_TO_BITS), sload(storagePointer)) } uint256 wordRemainder; @@ -296,7 +297,7 @@ library Storage { // Read from the next slot if field spans 2 slots if (length > wordRemainder) { assembly { - result := or(result, shr(mul(wordRemainder, 8), sload(add(storagePointer, 1)))) + result := or(result, shr(mul(wordRemainder, BYTE_TO_BITS), sload(add(storagePointer, 1)))) } } } diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 60e6e993c0..ff64ea8598 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -298,11 +298,10 @@ library StoreCore { bytes memory dynamicData, FieldLayout fieldLayout ) internal { - // Emit event to notify indexers - emit Store_SetRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData); - // Early return if the table is an offchain table - if (tableId.getType() != RESOURCE_TABLE) { + if (tableId.getType() == RESOURCE_OFFCHAIN_TABLE) { + // Emit event to notify indexers + emit Store_SetRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData); return; } @@ -322,14 +321,17 @@ library StoreCore { } } + // Emit event to notify indexers + emit Store_SetRecord(tableId, keyTuple, staticData, encodedLengths, dynamicData); + // Store the static data at the static data location uint256 staticDataLocation = StoreCoreInternal._getStaticDataLocation(tableId, keyTuple); uint256 memoryPointer = Memory.dataPointer(staticData); Storage.store({ storagePointer: staticDataLocation, offset: 0, - memoryPointer: memoryPointer, - length: staticData.length + length: staticData.length, + memoryPointer: memoryPointer }); // Set the dynamic data if there are dynamic fields @@ -350,8 +352,8 @@ library StoreCore { Storage.store({ storagePointer: dynamicDataLocation, offset: 0, - memoryPointer: memoryPointer, - length: dynamicDataLength + length: dynamicDataLength, + memoryPointer: memoryPointer }); memoryPointer += dynamicDataLength; // move the memory pointer to the start of the next dynamic data unchecked { @@ -387,16 +389,15 @@ library StoreCore { * @param data The data to write to the static data of the record at the start byte. */ function spliceStaticData(ResourceId tableId, bytes32[] memory keyTuple, uint48 start, bytes memory data) internal { - uint256 location = StoreCoreInternal._getStaticDataLocation(tableId, keyTuple); - - // Emit event to notify offchain indexers - emit StoreCore.Store_SpliceStaticData({ tableId: tableId, keyTuple: keyTuple, start: start, data: data }); - // Early return if the table is an offchain table - if (tableId.getType() != RESOURCE_TABLE) { + if (tableId.getType() == RESOURCE_OFFCHAIN_TABLE) { + // Emit event to notify offchain indexers + emit StoreCore.Store_SpliceStaticData({ tableId: tableId, keyTuple: keyTuple, start: start, data: data }); return; } + uint256 location = StoreCoreInternal._getStaticDataLocation(tableId, keyTuple); + // Call onBeforeSpliceStaticData hooks (before actually modifying the state, so observers have access to the previous state if needed) bytes21[] memory hooks = StoreHooks._get(tableId); for (uint256 i; i < hooks.length; i++) { @@ -411,6 +412,9 @@ library StoreCore { } } + // Emit event to notify offchain indexers + emit StoreCore.Store_SpliceStaticData({ tableId: tableId, keyTuple: keyTuple, start: start, data: data }); + // Store the provided value in storage Storage.store({ storagePointer: location, offset: start, data: data }); @@ -583,11 +587,10 @@ library StoreCore { * @param fieldLayout The field layout for the record. */ function deleteRecord(ResourceId tableId, bytes32[] memory keyTuple, FieldLayout fieldLayout) internal { - // Emit event to notify indexers - emit Store_DeleteRecord(tableId, keyTuple); - // Early return if the table is an offchain table - if (tableId.getType() != RESOURCE_TABLE) { + if (tableId.getType() == RESOURCE_OFFCHAIN_TABLE) { + // Emit event to notify indexers + emit Store_DeleteRecord(tableId, keyTuple); return; } @@ -600,6 +603,9 @@ library StoreCore { } } + // Emit event to notify indexers + emit Store_DeleteRecord(tableId, keyTuple); + // Delete static data uint256 staticDataLocation = StoreCoreInternal._getStaticDataLocation(tableId, keyTuple); Storage.store({ storagePointer: staticDataLocation, offset: 0, data: new bytes(fieldLayout.staticDataLength()) }); @@ -740,7 +746,7 @@ library StoreCore { for (uint8 i; i < numDynamicFields; i++) { uint256 dynamicDataLocation = StoreCoreInternal._getDynamicDataLocation(tableId, keyTuple, i); uint256 length = encodedLengths.atIndex(i); - Storage.load({ storagePointer: dynamicDataLocation, length: length, offset: 0, memoryPointer: memoryPointer }); + Storage.load({ storagePointer: dynamicDataLocation, offset: 0, length: length, memoryPointer: memoryPointer }); // Advance memoryPointer by the length of this dynamic field memoryPointer += length; } @@ -828,8 +834,8 @@ library StoreCore { return Storage.load({ storagePointer: StoreCoreInternal._getDynamicDataLocation(tableId, keyTuple, dynamicFieldIndex), - length: StoreCoreInternal._loadEncodedDynamicDataLength(tableId, keyTuple).atIndex(dynamicFieldIndex), - offset: 0 + offset: 0, + length: StoreCoreInternal._loadEncodedDynamicDataLength(tableId, keyTuple).atIndex(dynamicFieldIndex) }); } @@ -917,7 +923,7 @@ library StoreCore { // Get the length and storage location of the dynamic field uint256 location = StoreCoreInternal._getDynamicDataLocation(tableId, keyTuple, dynamicFieldIndex); - return Storage.load({ storagePointer: location, length: end - start, offset: start }); + return Storage.load({ storagePointer: location, offset: start, length: end - start }); } } @@ -989,6 +995,23 @@ library StoreCoreInternal { // Update the encoded length PackedCounter updatedEncodedLengths = previousEncodedLengths.setAtIndex(dynamicFieldIndex, updatedFieldLength); + // Call onBeforeSpliceDynamicData hooks (before actually modifying the state, so observers have access to the previous state if needed) + bytes21[] memory hooks = StoreHooks._get(tableId); + for (uint256 i; i < hooks.length; i++) { + Hook hook = Hook.wrap(hooks[i]); + if (hook.isEnabled(BEFORE_SPLICE_DYNAMIC_DATA)) { + IStoreHook(hook.getAddress()).onBeforeSpliceDynamicData({ + tableId: tableId, + keyTuple: keyTuple, + dynamicFieldIndex: dynamicFieldIndex, + startWithinField: startWithinField, + deleteCount: deleteCount, + encodedLengths: previousEncodedLengths, + data: data + }); + } + } + { // Compute start index for the splice uint256 start = startWithinField; @@ -1010,23 +1033,6 @@ library StoreCoreInternal { }); } - // Call onBeforeSpliceDynamicData hooks (before actually modifying the state, so observers have access to the previous state if needed) - bytes21[] memory hooks = StoreHooks._get(tableId); - for (uint256 i; i < hooks.length; i++) { - Hook hook = Hook.wrap(hooks[i]); - if (hook.isEnabled(BEFORE_SPLICE_DYNAMIC_DATA)) { - IStoreHook(hook.getAddress()).onBeforeSpliceDynamicData({ - tableId: tableId, - keyTuple: keyTuple, - dynamicFieldIndex: dynamicFieldIndex, - startWithinField: startWithinField, - deleteCount: deleteCount, - encodedLengths: previousEncodedLengths, - data: data - }); - } - } - // Store the updated encoded lengths in storage if (previousFieldLength != updatedFieldLength) { uint256 dynamicSchemaLengthSlot = _getDynamicDataLengthLocation(tableId, keyTuple); @@ -1078,7 +1084,7 @@ library StoreCoreInternal { // Load the data from storage uint256 location = _getStaticDataLocation(tableId, keyTuple); - return Storage.load({ storagePointer: location, length: length, offset: 0 }); + return Storage.load({ storagePointer: location, offset: 0, length: length }); } /** @@ -1100,8 +1106,8 @@ library StoreCoreInternal { return Storage.load({ storagePointer: _getStaticDataLocation(tableId, keyTuple), - length: fieldLayout.atIndex(fieldIndex), - offset: _getStaticDataOffset(fieldLayout, fieldIndex) + offset: _getStaticDataOffset(fieldLayout, fieldIndex), + length: fieldLayout.atIndex(fieldIndex) }); } diff --git a/packages/store/src/codegen/tables/Hooks.sol b/packages/store/src/codegen/tables/Hooks.sol index ed1be30ddd..8f0f04d667 100644 --- a/packages/store/src/codegen/tables/Hooks.sol +++ b/packages/store/src/codegen/tables/Hooks.sol @@ -457,7 +457,7 @@ library Hooks { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bytes21[] memory hooks) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/store/src/codegen/tables/ResourceIds.sol b/packages/store/src/codegen/tables/ResourceIds.sol index 5b33c94693..81950d4941 100644 --- a/packages/store/src/codegen/tables/ResourceIds.sol +++ b/packages/store/src/codegen/tables/ResourceIds.sol @@ -211,7 +211,7 @@ library ResourceIds { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bool exists) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(exists); diff --git a/packages/store/src/codegen/tables/StoreHooks.sol b/packages/store/src/codegen/tables/StoreHooks.sol index 40e49c7770..72f9aab99d 100644 --- a/packages/store/src/codegen/tables/StoreHooks.sol +++ b/packages/store/src/codegen/tables/StoreHooks.sol @@ -462,7 +462,7 @@ library StoreHooks { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bytes21[] memory hooks) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/store/src/codegen/tables/Tables.sol b/packages/store/src/codegen/tables/Tables.sol index e62e947299..c3cfe5fed6 100644 --- a/packages/store/src/codegen/tables/Tables.sol +++ b/packages/store/src/codegen/tables/Tables.sol @@ -778,7 +778,7 @@ library Tables { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( FieldLayout fieldLayout, diff --git a/packages/store/src/leftMask.sol b/packages/store/src/leftMask.sol index 604ffd446d..9343cfc5a8 100644 --- a/packages/store/src/leftMask.sol +++ b/packages/store/src/leftMask.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; +import { BYTE_TO_BITS } from "./constants.sol"; + /** * @title Byte Mask Utility * @notice Utility functions to manage bytes in memory. @@ -24,6 +26,6 @@ pragma solidity >=0.8.21; */ function leftMask(uint256 byteLength) pure returns (uint256 mask) { unchecked { - return ~(type(uint256).max >> (byteLength * 8)); + return ~(type(uint256).max >> (byteLength * BYTE_TO_BITS)); } } diff --git a/packages/store/src/tightcoder/TightCoder.sol b/packages/store/src/tightcoder/TightCoder.sol index 7bd5858665..320792cbed 100644 --- a/packages/store/src/tightcoder/TightCoder.sol +++ b/packages/store/src/tightcoder/TightCoder.sol @@ -13,7 +13,7 @@ library TightCoder { * @dev Copies the array to a new bytes array, tightly packing its elements. * @param array The array to encode. * @param elementSize The size of each element in bytes. - * @param leftPaddingBits The number of bits to pad on the left for each element. + * @param leftPaddingBits The amount to shift each element to the left. * @return data A tightly packed byte array. * @notice elementSize and leftPaddingBits must be correctly provided by the caller based on the array's element type. */ diff --git a/packages/store/test/GasStorageLoad.t.sol b/packages/store/test/GasStorageLoad.t.sol index 7db60617b2..cfafdbcd65 100644 --- a/packages/store/test/GasStorageLoad.t.sol +++ b/packages/store/test/GasStorageLoad.t.sol @@ -71,38 +71,38 @@ contract GasStorageLoadTest is Test, GasReporter { bytes32 encodedFieldPartial = valuePartial; startGasReport("MUD storage load (cold, 1 word)"); - encodedSimple = Storage.load(SolidityStorage.STORAGE_SLOT_SIMPLE, encodedSimple.length, 0); + encodedSimple = Storage.load(SolidityStorage.STORAGE_SLOT_SIMPLE, 0, encodedSimple.length); endGasReport(); startGasReport("MUD storage load (cold, 1 word, partial)"); - encodedPartial = Storage.load(SolidityStorage.STORAGE_SLOT_PARTIAL, encodedPartial.length, 16); + encodedPartial = Storage.load(SolidityStorage.STORAGE_SLOT_PARTIAL, 16, encodedPartial.length); endGasReport(); startGasReport("MUD storage load (cold, 10 words)"); - encoded9Words = Storage.load(SolidityStorage.STORAGE_SLOT_BYTES, encoded9Words.length, 0); + encoded9Words = Storage.load(SolidityStorage.STORAGE_SLOT_BYTES, 0, encoded9Words.length); endGasReport(); // warm startGasReport("MUD storage load (warm, 1 word)"); - encodedSimple = Storage.load(SolidityStorage.STORAGE_SLOT_SIMPLE, encodedSimple.length, 0); + encodedSimple = Storage.load(SolidityStorage.STORAGE_SLOT_SIMPLE, 0, encodedSimple.length); endGasReport(); startGasReport("MUD storage load field (warm, 1 word)"); - encodedFieldSimple = Storage.loadField(SolidityStorage.STORAGE_SLOT_SIMPLE, encodedSimple.length, 0); + encodedFieldSimple = Storage.loadField(SolidityStorage.STORAGE_SLOT_SIMPLE, 0, encodedSimple.length); endGasReport(); startGasReport("MUD storage load (warm, 1 word, partial)"); - encodedPartial = Storage.load(SolidityStorage.STORAGE_SLOT_PARTIAL, encodedPartial.length, 16); + encodedPartial = Storage.load(SolidityStorage.STORAGE_SLOT_PARTIAL, 16, encodedPartial.length); endGasReport(); - encodedFieldPartial = Storage.loadField(SolidityStorage.STORAGE_SLOT_PARTIAL, encodedSimple.length, 16); + encodedFieldPartial = Storage.loadField(SolidityStorage.STORAGE_SLOT_PARTIAL, 16, encodedSimple.length); startGasReport("MUD storage load field (warm, 1 word, partial)"); - encodedFieldPartial = Storage.loadField(SolidityStorage.STORAGE_SLOT_PARTIAL, encodedSimple.length, 16); + encodedFieldPartial = Storage.loadField(SolidityStorage.STORAGE_SLOT_PARTIAL, 16, encodedSimple.length); endGasReport(); startGasReport("MUD storage load (warm, 10 words)"); - encoded9Words = Storage.load(SolidityStorage.STORAGE_SLOT_BYTES, encoded9Words.length, 0); + encoded9Words = Storage.load(SolidityStorage.STORAGE_SLOT_BYTES, 0, encoded9Words.length); endGasReport(); // Do something in case the optimizer removes unused assignments diff --git a/packages/store/test/Storage.t.sol b/packages/store/test/Storage.t.sol index 6d8e178a4e..6b2fb40c8f 100644 --- a/packages/store/test/Storage.t.sol +++ b/packages/store/test/Storage.t.sol @@ -59,7 +59,7 @@ contract StorageTest is Test, GasReporter { // Assert we can load the correct partial value from storage startGasReport("load 34 bytes over 3 storage slots (with offset and safeTrail))"); - bytes memory data = Storage.load({ storagePointer: storagePointer, length: 34, offset: 31 }); + bytes memory data = Storage.load({ storagePointer: storagePointer, offset: 31, length: 34 }); endGasReport(); assertEq(Bytes.slice1(data, 0), bytes1(0x01)); @@ -73,7 +73,7 @@ contract StorageTest is Test, GasReporter { vm.assume(storagePointer > 0); Storage.store({ storagePointer: uint256(storagePointer), offset: offset, data: data }); - assertEq(Storage.load({ storagePointer: uint256(storagePointer), length: data.length, offset: offset }), data); + assertEq(Storage.load({ storagePointer: uint256(storagePointer), offset: offset, length: data.length }), data); } function testStoreLoadFieldBytes32Fuzzy(bytes32 data, uint256 storagePointer, uint256 offset) public { @@ -126,7 +126,7 @@ contract StorageTest is Test, GasReporter { uint256 storagePointer = uint256(keccak256("some location")); Storage.store({ storagePointer: storagePointer, offset: 10, data: abi.encodePacked(dataAfterLoad) }); - Storage.load({ storagePointer: storagePointer, length: 10, offset: 10, memoryPointer: memoryPointer }); + Storage.load({ storagePointer: storagePointer, offset: 10, length: 10, memoryPointer: memoryPointer }); assertEq(testData, expectedData); } diff --git a/packages/store/test/StoreCore.t.sol b/packages/store/test/StoreCore.t.sol index ac1dadda11..73d076ad2b 100644 --- a/packages/store/test/StoreCore.t.sol +++ b/packages/store/test/StoreCore.t.sol @@ -17,7 +17,7 @@ import { StoreSwitch } from "../src/StoreSwitch.sol"; import { IStoreHook } from "../src/IStoreHook.sol"; import { Tables, ResourceIds, TablesTableId } from "../src/codegen/index.sol"; import { ResourceId, ResourceIdLib, ResourceIdInstance } from "../src/ResourceId.sol"; -import { RESOURCE_TABLE } from "../src/storeResourceTypes.sol"; +import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "../src/storeResourceTypes.sol"; import { FieldLayoutEncodeHelper } from "./FieldLayoutEncodeHelper.sol"; import { BEFORE_SET_RECORD, AFTER_SET_RECORD, BEFORE_SPLICE_STATIC_DATA, AFTER_SPLICE_STATIC_DATA, BEFORE_SPLICE_DYNAMIC_DATA, AFTER_SPLICE_DYNAMIC_DATA, BEFORE_DELETE_RECORD, AFTER_DELETE_RECORD, ALL, BEFORE_ALL, AFTER_ALL } from "../src/storeHookTypes.sol"; import { SchemaEncodeHelper } from "./SchemaEncodeHelper.sol"; @@ -44,6 +44,7 @@ contract StoreCoreTest is Test, StoreMock { string[] defaultKeyNames = new string[](1); ResourceId _tableId = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some table" }); ResourceId _tableId2 = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some other table" }); + ResourceId _tableId3 = ResourceIdLib.encode({ typeId: RESOURCE_OFFCHAIN_TABLE, name: "some offchain table" }); function testGetStaticDataLocation() public { ResourceId tableId = _tableId; @@ -131,8 +132,9 @@ contract StoreCoreTest is Test, StoreMock { vm.expectRevert( abi.encodeWithSelector( - FieldLayoutLib.FieldLayoutLib_InvalidLength.selector, - invalidFieldLayout.numDynamicFields() + FieldLayoutLib.FieldLayoutLib_TooManyDynamicFields.selector, + invalidFieldLayout.numDynamicFields(), + 5 ) ); IStore(this).registerTable( @@ -1317,4 +1319,88 @@ contract StoreCoreTest is Test, StoreMock { assertEq(loadedData.encodedLengths.unwrap(), bytes32(0)); assertEq(loadedData.dynamicData, ""); } + + function testSetDataOffchainTable() public { + ResourceId tableId = _tableId3; + + // Register offchain table + FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(1, 2, 1, 2, 0); + Schema valueSchema = SchemaEncodeHelper.encode( + SchemaType.UINT8, + SchemaType.UINT16, + SchemaType.UINT8, + SchemaType.UINT16 + ); + + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4)); + + // Set data + bytes memory staticData = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); + + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = "some key"; + + // Expect a Store_SetRecord event to be emitted + vm.expectEmit(true, true, true, true); + emit Store_SetRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0)); + + IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0)); + } + + function testDeleteDataOffchainTable() public { + ResourceId tableId = _tableId3; + + // Register table + FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 2); + { + Schema valueSchema = SchemaEncodeHelper.encode( + SchemaType.UINT128, + SchemaType.UINT32_ARRAY, + SchemaType.UINT32_ARRAY + ); + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3)); + } + + bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); + + bytes memory secondDataBytes; + { + uint32[] memory secondData = new uint32[](2); + secondData[0] = 0x11121314; + secondData[1] = 0x15161718; + secondDataBytes = EncodeArray.encode(secondData); + } + + bytes memory thirdDataBytes; + { + uint32[] memory thirdData = new uint32[](3); + thirdData[0] = 0x191a1b1c; + thirdData[1] = 0x1d1e1f20; + thirdData[2] = 0x21222324; + thirdDataBytes = EncodeArray.encode(thirdData); + } + + PackedCounter encodedDynamicLength; + { + encodedDynamicLength = PackedCounterLib.pack(uint40(secondDataBytes.length), uint40(thirdDataBytes.length)); + } + + // Concat data + bytes memory staticData = abi.encodePacked(firstDataBytes); + bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes); + + // Create keyTuple + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = bytes32("some key"); + + // Set data + IStore(this).setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData); + + // Expect a Store_DeleteRecord event to be emitted + vm.expectEmit(true, true, true, true); + emit Store_DeleteRecord(tableId, keyTuple); + + // Delete data + IStore(this).deleteRecord(tableId, keyTuple); + } } diff --git a/packages/store/test/StoreCoreGas.t.sol b/packages/store/test/StoreCoreGas.t.sol index 3b65a03fc2..67962c21b1 100644 --- a/packages/store/test/StoreCoreGas.t.sol +++ b/packages/store/test/StoreCoreGas.t.sol @@ -16,7 +16,7 @@ import { IStoreErrors } from "../src/IStoreErrors.sol"; import { IStore } from "../src/IStore.sol"; import { ResourceId, ResourceIdLib } from "../src/ResourceId.sol"; import { ResourceIds } from "../src/codegen/tables/ResourceIds.sol"; -import { RESOURCE_TABLE } from "../src/storeResourceTypes.sol"; +import { RESOURCE_TABLE, RESOURCE_OFFCHAIN_TABLE } from "../src/storeResourceTypes.sol"; import { FieldLayoutEncodeHelper } from "./FieldLayoutEncodeHelper.sol"; import { SchemaEncodeHelper } from "./SchemaEncodeHelper.sol"; import { StoreMock } from "./StoreMock.sol"; @@ -37,6 +37,7 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { Schema defaultKeySchema = SchemaEncodeHelper.encode(SchemaType.BYTES32); ResourceId _tableId = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some table" }); ResourceId _tableId2 = ResourceIdLib.encode({ typeId: RESOURCE_TABLE, name: "some other table" }); + ResourceId _tableId3 = ResourceIdLib.encode({ typeId: RESOURCE_OFFCHAIN_TABLE, name: "some offchain table" }); function testGetStaticDataLocation() public { ResourceId tableId = _tableId; @@ -724,4 +725,92 @@ contract StoreCoreGasTest is Test, GasReporter, StoreMock { StoreCore.deleteRecord(tableId, keyTuple); endGasReport(); } + + function testSetDataOffchainTable() public { + ResourceId tableId = _tableId3; + + // Register offchain table + FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(1, 2, 1, 2, 0); + Schema valueSchema = SchemaEncodeHelper.encode( + SchemaType.UINT8, + SchemaType.UINT16, + SchemaType.UINT8, + SchemaType.UINT16 + ); + + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](4)); + + // Set data + bytes memory staticData = abi.encodePacked(bytes1(0x01), bytes2(0x0203), bytes1(0x04), bytes2(0x0506)); + + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = "some key"; + + // Expect a Store_SetRecord event to be emitted + vm.expectEmit(true, true, true, true); + emit Store_SetRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0)); + + startGasReport("StoreCore: set record in offchain table"); + IStore(this).setRecord(tableId, keyTuple, staticData, PackedCounter.wrap(bytes32(0)), new bytes(0)); + endGasReport(); + } + + function testDeleteDataOffchainTable() public { + ResourceId tableId = _tableId3; + + // Register table + FieldLayout fieldLayout = FieldLayoutEncodeHelper.encode(16, 2); + { + Schema valueSchema = SchemaEncodeHelper.encode( + SchemaType.UINT128, + SchemaType.UINT32_ARRAY, + SchemaType.UINT32_ARRAY + ); + IStore(this).registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](3)); + } + + bytes16 firstDataBytes = bytes16(0x0102030405060708090a0b0c0d0e0f10); + + bytes memory secondDataBytes; + { + uint32[] memory secondData = new uint32[](2); + secondData[0] = 0x11121314; + secondData[1] = 0x15161718; + secondDataBytes = EncodeArray.encode(secondData); + } + + bytes memory thirdDataBytes; + { + uint32[] memory thirdData = new uint32[](3); + thirdData[0] = 0x191a1b1c; + thirdData[1] = 0x1d1e1f20; + thirdData[2] = 0x21222324; + thirdDataBytes = EncodeArray.encode(thirdData); + } + + PackedCounter encodedDynamicLength; + { + encodedDynamicLength = PackedCounterLib.pack(uint40(secondDataBytes.length), uint40(thirdDataBytes.length)); + } + + // Concat data + bytes memory staticData = abi.encodePacked(firstDataBytes); + bytes memory dynamicData = abi.encodePacked(secondDataBytes, thirdDataBytes); + + // Create keyTuple + bytes32[] memory keyTuple = new bytes32[](1); + keyTuple[0] = bytes32("some key"); + + // Set data + IStore(this).setRecord(tableId, keyTuple, staticData, encodedDynamicLength, dynamicData); + + // Expect a Store_DeleteRecord event to be emitted + vm.expectEmit(true, true, true, true); + emit Store_DeleteRecord(tableId, keyTuple); + + // Delete data + startGasReport("StoreCore: delete record in offchain table"); + IStore(this).deleteRecord(tableId, keyTuple); + endGasReport(); + } } diff --git a/packages/store/test/codegen/tables/Callbacks.sol b/packages/store/test/codegen/tables/Callbacks.sol index 5d3552e1b8..ec96589c0c 100644 --- a/packages/store/test/codegen/tables/Callbacks.sol +++ b/packages/store/test/codegen/tables/Callbacks.sol @@ -459,7 +459,7 @@ library Callbacks { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bytes24[] memory value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/store/test/codegen/tables/KeyEncoding.sol b/packages/store/test/codegen/tables/KeyEncoding.sol index 44ab473659..08c517307d 100644 --- a/packages/store/test/codegen/tables/KeyEncoding.sol +++ b/packages/store/test/codegen/tables/KeyEncoding.sol @@ -299,7 +299,7 @@ library KeyEncoding { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bool value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/packages/store/test/codegen/tables/Mixed.sol b/packages/store/test/codegen/tables/Mixed.sol index f772c1ea84..cffb5500b3 100644 --- a/packages/store/test/codegen/tables/Mixed.sol +++ b/packages/store/test/codegen/tables/Mixed.sol @@ -700,7 +700,7 @@ library Mixed { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( uint32 u32, diff --git a/packages/store/test/codegen/tables/Vector2.sol b/packages/store/test/codegen/tables/Vector2.sol index e39a5c9440..a22765840f 100644 --- a/packages/store/test/codegen/tables/Vector2.sol +++ b/packages/store/test/codegen/tables/Vector2.sol @@ -328,7 +328,7 @@ library Vector2 { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32 x, uint32 y) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(x, y); diff --git a/packages/store/ts/codegen/field.ts b/packages/store/ts/codegen/field.ts index 4ccc255669..e24f5403a6 100644 --- a/packages/store/ts/codegen/field.ts +++ b/packages/store/ts/codegen/field.ts @@ -25,7 +25,7 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithFieldSuffix(options.withSuffixlessFieldMethods, field.name, (_methodNameSuffix) => renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => ` /** * @notice Get ${field.name}${_commentSuffix}. */ @@ -57,7 +57,7 @@ export function renderFieldMethods(options: RenderTableOptions) { } result += renderWithFieldSuffix(options.withSuffixlessFieldMethods, field.name, (_methodNameSuffix) => - renderWithStore(storeArgument, (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => { + renderWithStore(storeArgument, ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => { const externalArguments = renderArguments([_typedStore, _typedTableId, _typedKeyArgs, _typedFieldName]); const setFieldMethod = field.isDynamic ? "setDynamicField" : "setStaticField"; const encodeFieldSingle = renderEncodeFieldSingle(field); @@ -85,7 +85,7 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithFieldSuffix(options.withSuffixlessFieldMethods, field.name, (_methodNameSuffix) => renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => ` /** * @notice Get the length of ${field.name}${_commentSuffix}. */ @@ -107,7 +107,7 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithFieldSuffix(options.withSuffixlessFieldMethods, field.name, (_methodNameSuffix) => renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => ` /** * @notice Get an item of ${field.name}${_commentSuffix}. * @dev Reverts with Store_IndexOutOfBounds if \`_index\` is out of bounds for the array. @@ -138,7 +138,7 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithFieldSuffix(options.withSuffixlessFieldMethods, field.name, (_methodNameSuffix) => renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => ` /** * @notice Push ${portionData.title} to ${field.name}${_commentSuffix}. */ @@ -158,7 +158,7 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithFieldSuffix(options.withSuffixlessFieldMethods, field.name, (_methodNameSuffix) => renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => ` /** * @notice Pop ${portionData.title} from ${field.name}${_commentSuffix}. */ @@ -175,7 +175,7 @@ export function renderFieldMethods(options: RenderTableOptions) { ); result += renderWithFieldSuffix(options.withSuffixlessFieldMethods, field.name, (_methodNameSuffix) => - renderWithStore(storeArgument, (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => { + renderWithStore(storeArgument, ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => { const externalArguments = renderArguments([ _typedStore, _typedTableId, @@ -253,13 +253,12 @@ function renderCastStaticBytesToType(field: RenderType, staticBytes: string) { /** bytes/string are dynamic, but aren't really arrays */ function fieldPortionData(field: RenderField) { - const methodNameSuffix = ""; if (field.arrayElement) { const name = "_element"; - const elementFieldData = { ...field.arrayElement, arrayElement: undefined, name, methodNameSuffix }; + const elementFieldData = { ...field.arrayElement, arrayElement: undefined, name }; return { typeWithLocation: field.arrayElement.typeWithLocation, - name: "_element", + name, encoded: renderEncodeFieldSingle(elementFieldData), decoded: renderDecodeFieldSingle(elementFieldData), title: "an element", @@ -267,7 +266,7 @@ function fieldPortionData(field: RenderField) { }; } else { const name = "_slice"; - const elementFieldData = { ...field, name, methodNameSuffix }; + const elementFieldData = { ...field, name }; return { typeWithLocation: `${field.typeId} memory`, name, diff --git a/packages/store/ts/codegen/record.ts b/packages/store/ts/codegen/record.ts index c083d5f2ed..33d726ffdc 100644 --- a/packages/store/ts/codegen/record.ts +++ b/packages/store/ts/codegen/record.ts @@ -17,7 +17,7 @@ export function renderRecordMethods(options: RenderTableOptions) { if (options.withGetters) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => ` /** * @notice Get the full data${_commentSuffix}. */ @@ -41,7 +41,7 @@ export function renderRecordMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix, _internal) => { + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix, _useExplicitFieldLayout }) => { const externalArguments = renderArguments([ _typedStore, _typedTableId, @@ -50,7 +50,8 @@ export function renderRecordMethods(options: RenderTableOptions) { ]); const internalArguments = - "_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData" + (_internal ? ", _fieldLayout" : ""); + "_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData" + + (_useExplicitFieldLayout ? ", _fieldLayout" : ""); return ` /** @@ -70,7 +71,7 @@ export function renderRecordMethods(options: RenderTableOptions) { if (structName !== undefined) { result += renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix, _internal) => { + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix, _useExplicitFieldLayout }) => { const externalArguments = renderArguments([ _typedStore, _typedTableId, @@ -79,7 +80,8 @@ export function renderRecordMethods(options: RenderTableOptions) { ]); const internalArguments = - "_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData" + (_internal ? ", _fieldLayout" : ""); + "_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData" + + (_useExplicitFieldLayout ? ", _fieldLayout" : ""); return ` /** @@ -139,19 +141,19 @@ export function renderDeleteRecordMethods(options: RenderTableOptions) { return renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix, _internal) => { + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix, _useExplicitFieldLayout }) => { const externalArguments = renderArguments([_typedStore, _typedTableId, _typedKeyArgs]); - const internalArguments = "_tableId, _keyTuple" + (_internal ? ", _fieldLayout" : ""); + const internalArguments = "_tableId, _keyTuple" + (_useExplicitFieldLayout ? ", _fieldLayout" : ""); return ` - /** - * @notice Delete all data for given keys${_commentSuffix}. - */ - function ${_methodNamePrefix}deleteRecord(${externalArguments}) internal { - ${_keyTupleDefinition} - ${_store}.deleteRecord(${internalArguments}); - } - `; + /** + * @notice Delete all data for given keys${_commentSuffix}. + */ + function ${_methodNamePrefix}deleteRecord(${externalArguments}) internal { + ${_keyTupleDefinition} + ${_store}.deleteRecord(${internalArguments}); + } + `; } ); } diff --git a/packages/store/ts/codegen/renderFieldLayout.ts b/packages/store/ts/codegen/renderFieldLayout.ts index cb112a875c..b40cc998cb 100644 --- a/packages/store/ts/codegen/renderFieldLayout.ts +++ b/packages/store/ts/codegen/renderFieldLayout.ts @@ -14,8 +14,8 @@ export function encodeFieldLayout(fields: RenderType[]) { let totalLength = 0; const totalFields = fields.length; - if (totalFields > MAX_TOTAL_FIELDS) throw new Error(`FieldLayout: invalid length ${totalFields}`); - if (numDynamicFields > MAX_DYNAMIC_FIELDS) throw new Error(`FieldLayout: invalid length ${numDynamicFields}`); + if (totalFields > MAX_TOTAL_FIELDS) throw new Error("FieldLayout: too many fields"); + if (numDynamicFields > MAX_DYNAMIC_FIELDS) throw new Error("FieldLayout: too many dynamic fields"); for (let i = 0; i < staticFields.length; i++) { const { isDynamic, staticByteLength } = fields[i]; diff --git a/packages/store/ts/codegen/renderTable.ts b/packages/store/ts/codegen/renderTable.ts index f6b06822a0..52ba690c3a 100644 --- a/packages/store/ts/codegen/renderTable.ts +++ b/packages/store/ts/codegen/renderTable.ts @@ -126,7 +126,7 @@ export function renderTable(options: RenderTableOptions) { ${renderWithStore( storeArgument, - (_typedStore, _store, _commentSuffix, _untypedStore, _methodNamePrefix) => ` + ({ _typedStore, _store, _commentSuffix, _methodNamePrefix }) => ` /** * @notice Register the table with its config${_commentSuffix}. */ @@ -144,7 +144,7 @@ export function renderTable(options: RenderTableOptions) { ${renderEncodeStatic(staticFields)} - ${renderEncodedLengths(dynamicFields)} + ${renderEncodeLengths(dynamicFields)} ${renderEncodeDynamic(dynamicFields)} @@ -152,7 +152,7 @@ export function renderTable(options: RenderTableOptions) { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(${renderArguments( options.fields.map(({ name, typeWithLocation }) => `${typeWithLocation} ${name}`) @@ -191,7 +191,7 @@ function renderEncodeStatic(staticFields: RenderStaticField[]) { `; } -function renderEncodedLengths(dynamicFields: RenderDynamicField[]) { +function renderEncodeLengths(dynamicFields: RenderDynamicField[]) { if (dynamicFields.length === 0) return ""; return ` diff --git a/packages/store/ts/codegen/renderTableIndex.ts b/packages/store/ts/codegen/renderTableIndex.ts index 3ad6a51cb0..d1eeaa5a23 100644 --- a/packages/store/ts/codegen/renderTableIndex.ts +++ b/packages/store/ts/codegen/renderTableIndex.ts @@ -8,7 +8,7 @@ export function renderTableIndex(options: TableOptions[]) { ${renderList(options, ({ outputPath, tableName, renderOptions: { structName, staticResourceData } }) => { const imports = [tableName]; if (structName) imports.push(structName); - if (staticResourceData) imports.push(`${tableName}TableId`); + if (staticResourceData) imports.push(staticResourceData.tableIdName); return `import { ${imports.join(", ")} } from "./${posixPath(outputPath)}";`; })} diff --git a/packages/store/ts/codegen/tableOptions.ts b/packages/store/ts/codegen/tableOptions.ts index 9e3cff168e..95ae6ffbb4 100644 --- a/packages/store/ts/codegen/tableOptions.ts +++ b/packages/store/ts/codegen/tableOptions.ts @@ -75,18 +75,14 @@ export function getTableOptions( // With tableIdArgument: tableId is a dynamic argument for each method // Without tableIdArgument: tableId is a file-level constant generated from `staticResourceData` - const staticResourceData = (() => { - if (tableData.tableIdArgument) { - return; - } else { - return { + const staticResourceData = tableData.tableIdArgument + ? undefined + : { tableIdName: tableName + "TableId", namespace: config.namespace, name: tableData.name, offchainOnly: tableData.offchainOnly, }; - } - })(); options.push({ outputPath: path.join(tableData.directory, `${tableName}.sol`), diff --git a/packages/store/ts/codegen/tightcoder/renderFunctions.ts b/packages/store/ts/codegen/tightcoder/renderFunctions.ts index 7b8b591e54..eefaa21805 100644 --- a/packages/store/ts/codegen/tightcoder/renderFunctions.ts +++ b/packages/store/ts/codegen/tightcoder/renderFunctions.ts @@ -1,6 +1,6 @@ -import { getLeftPaddingBits } from "@latticexyz/common/codegen"; +import { RenderType, getLeftPaddingBits } from "@latticexyz/common/codegen"; -export function renderTightCoderDecode(element: { internalTypeId: string; staticByteLength: number }) { +export function renderTightCoderDecode(element: Pick) { return ` /** * @notice Decodes a slice into an array of ${element.internalTypeId}. @@ -25,7 +25,7 @@ export function renderTightCoderDecode(element: { internalTypeId: string; static `; } -export function renderTightCoderEncode(element: { internalTypeId: string; staticByteLength: number }) { +export function renderTightCoderEncode(element: Pick) { return ` /** diff --git a/packages/store/ts/codegen/types.ts b/packages/store/ts/codegen/types.ts index 5238965664..164b512afa 100644 --- a/packages/store/ts/codegen/types.ts +++ b/packages/store/ts/codegen/types.ts @@ -14,7 +14,7 @@ export interface RenderTableOptions { libraryName: string; /** Name of the struct to render. If undefined, struct and its methods aren't rendered. */ structName?: string; - /** Data used to statically registed the table. If undefined, all methods receive `_tableId` as an argument. */ + /** Data used to statically register the table. If undefined, all methods receive `_tableId` as an argument. */ staticResourceData?: StaticResourceData; /** Path for store package imports */ storeImportPath: string; diff --git a/packages/store/ts/codegen/userType.ts b/packages/store/ts/codegen/userType.ts index 75de3826c6..72e613df37 100644 --- a/packages/store/ts/codegen/userType.ts +++ b/packages/store/ts/codegen/userType.ts @@ -8,8 +8,6 @@ import { parseStaticArray } from "@latticexyz/config"; import { ImportDatum, RenderType, SolidityUserDefinedType } from "@latticexyz/common/codegen"; import { StoreConfig } from "../config"; -export type UserTypeInfo = ReturnType; - /** * Resolve an abi or user type into a SchemaType and RenderType */ @@ -148,7 +146,7 @@ export function getUserTypeInfo( isDynamic: false, typeWrap: `${typeId}.wrap`, typeUnwrap: `${typeId}.unwrap`, - internalTypeId: `${solidityUserType.internalTypeId}`, + internalTypeId: solidityUserType.internalTypeId, }, }; } diff --git a/packages/world-modules/gas-report.json b/packages/world-modules/gas-report.json index 9be66918a1..885089bac9 100644 --- a/packages/world-modules/gas-report.json +++ b/packages/world-modules/gas-report.json @@ -3,139 +3,139 @@ "file": "test/ERC20.t.sol", "test": "testApprove", "name": "approve", - "gasUsed": 114329 + "gasUsed": 114366 }, { "file": "test/ERC20.t.sol", "test": "testBurn", "name": "burn", - "gasUsed": 75866 + "gasUsed": 75931 }, { "file": "test/ERC20.t.sol", "test": "testMint", "name": "mint", - "gasUsed": 161705 + "gasUsed": 161770 }, { "file": "test/ERC20.t.sol", "test": "testTransfer", "name": "transfer", - "gasUsed": 92948 + "gasUsed": 93016 }, { "file": "test/ERC20.t.sol", "test": "testTransferFrom", "name": "transferFrom", - "gasUsed": 130250 + "gasUsed": 130355 }, { "file": "test/ERC721.t.sol", "test": "testApproveAllGas", "name": "setApprovalForAll", - "gasUsed": 113956 + "gasUsed": 113993 }, { "file": "test/ERC721.t.sol", "test": "testApproveGas", "name": "approve", - "gasUsed": 87965 + "gasUsed": 87999 }, { "file": "test/ERC721.t.sol", "test": "testBurnGas", "name": "burn", - "gasUsed": 101835 + "gasUsed": 101937 }, { "file": "test/ERC721.t.sol", "test": "testMintGas", "name": "mint", - "gasUsed": 169433 + "gasUsed": 169501 }, { "file": "test/ERC721.t.sol", "test": "testSafeMintToEOAGas", "name": "safeMint", - "gasUsed": 169704 + "gasUsed": 169772 }, { "file": "test/ERC721.t.sol", "test": "testSafeTransferFromToEOAGas", "name": "safeTransferFrom", - "gasUsed": 143638 + "gasUsed": 143774 }, { "file": "test/ERC721.t.sol", "test": "testTransferFromGas", "name": "transferFrom", - "gasUsed": 136798 + "gasUsed": 136934 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallComposite", "name": "install keys in table module", - "gasUsed": 1413089 + "gasUsed": 1413433 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "install keys in table module", - "gasUsed": 1413089 + "gasUsed": 1413433 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 158814 + "gasUsed": 158854 }, { "file": "test/KeysInTableModule.t.sol", "test": "testInstallSingleton", "name": "install keys in table module", - "gasUsed": 1413089 + "gasUsed": 1413433 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "install keys in table module", - "gasUsed": 1413089 + "gasUsed": 1413433 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "change a composite record on a table with keysInTableModule installed", - "gasUsed": 22497 + "gasUsed": 22492 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 155729 + "gasUsed": 155971 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "install keys in table module", - "gasUsed": 1413089 + "gasUsed": 1413433 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "change a record on a table with keysInTableModule installed", - "gasUsed": 21219 + "gasUsed": 21214 }, { "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "gasUsed": 84971 + "gasUsed": 85089 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testGetKeysWithValueGas", "name": "install keys with value module", - "gasUsed": 653436 + "gasUsed": 668206 }, { "file": "test/KeysWithValueModule.t.sol", @@ -153,49 +153,49 @@ "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "install keys with value module", - "gasUsed": 653436 + "gasUsed": 668206 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testInstall", "name": "set a record on a table with KeysWithValueModule installed", - "gasUsed": 135405 + "gasUsed": 135437 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "install keys with value module", - "gasUsed": 653436 + "gasUsed": 668206 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "change a record on a table with KeysWithValueModule installed", - "gasUsed": 103789 + "gasUsed": 103840 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetAndDeleteRecordHook", "name": "delete a record on a table with KeysWithValueModule installed", - "gasUsed": 36471 + "gasUsed": 36489 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "install keys with value module", - "gasUsed": 653436 + "gasUsed": 668206 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "set a field on a table with KeysWithValueModule installed", - "gasUsed": 146626 + "gasUsed": 146672 }, { "file": "test/KeysWithValueModule.t.sol", "test": "testSetField", "name": "change a field on a table with KeysWithValueModule installed", - "gasUsed": 111385 + "gasUsed": 111431 }, { "file": "test/query.t.sol", @@ -267,60 +267,60 @@ "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "register a callbound delegation", - "gasUsed": 118295 + "gasUsed": 118347 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromCallboundDelegation", "name": "call a system via a callbound delegation", - "gasUsed": 36688 + "gasUsed": 36697 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromSystemDelegation", "name": "register a systembound delegation", - "gasUsed": 115851 + "gasUsed": 115900 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromSystemDelegation", "name": "call a system via a systembound delegation", - "gasUsed": 33831 + "gasUsed": 33869 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "register a timebound delegation", - "gasUsed": 112789 + "gasUsed": 112823 }, { "file": "test/StandardDelegationsModule.t.sol", "test": "testCallFromTimeboundDelegation", "name": "call a system via a timebound delegation", - "gasUsed": 26815 + "gasUsed": 26809 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "install unique entity module", - "gasUsed": 681732 + "gasUsed": 694917 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstall", "name": "get a unique entity nonce (non-root module)", - "gasUsed": 51026 + "gasUsed": 51045 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "installRoot unique entity module", - "gasUsed": 648129 + "gasUsed": 663866 }, { "file": "test/UniqueEntityModule.t.sol", "test": "testInstallRoot", "name": "get a unique entity nonce (root module)", - "gasUsed": 51026 + "gasUsed": 51045 } ] diff --git a/packages/world-modules/src/modules/erc20-puppet/ERC20Module.sol b/packages/world-modules/src/modules/erc20-puppet/ERC20Module.sol index 5a65cd5af5..7a5459f2cb 100644 --- a/packages/world-modules/src/modules/erc20-puppet/ERC20Module.sol +++ b/packages/world-modules/src/modules/erc20-puppet/ERC20Module.sol @@ -6,6 +6,7 @@ import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { Module } from "@latticexyz/world/src/Module.sol"; import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; +import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; import { Puppet } from "../puppet/Puppet.sol"; import { createPuppet } from "../puppet/createPuppet.sol"; @@ -53,7 +54,10 @@ contract ERC20Module is Module { // Register the ERC20 tables and system IBaseWorld world = IBaseWorld(_world()); - registrationLibrary.delegatecall(abi.encodeCall(ERC20ModuleRegistrationLibrary.register, (world, namespace))); + (bool success, bytes memory returnData) = registrationLibrary.delegatecall( + abi.encodeCall(ERC20ModuleRegistrationLibrary.register, (world, namespace)) + ); + if (!success) revertWithBytes(returnData); // Initialize the Metadata ERC20Metadata.set(_metadataTableId(namespace), metadata); @@ -68,6 +72,7 @@ contract ERC20Module is Module { // Register the ERC20 in the ERC20Registry if (!ResourceIds.getExists(ERC20_REGISTRY_TABLE_ID)) { + world.registerNamespace(MODULE_NAMESPACE_ID); ERC20Registry.register(ERC20_REGISTRY_TABLE_ID); } ERC20Registry.set(ERC20_REGISTRY_TABLE_ID, namespaceId, puppet); @@ -83,6 +88,12 @@ contract ERC20ModuleRegistrationLibrary { * Register systems and tables for a new ERC20 token in a given namespace */ function register(IBaseWorld world, bytes14 namespace) public { + // Register the namespace if it doesn't exist yet + ResourceId tokenNamespace = WorldResourceIdLib.encodeNamespace(namespace); + if (!ResourceIds.getExists(tokenNamespace)) { + world.registerNamespace(tokenNamespace); + } + // Register the tables Allowances.register(_allowancesTableId(namespace)); Balances.register(_balancesTableId(namespace)); diff --git a/packages/world-modules/src/modules/erc20-puppet/tables/Allowances.sol b/packages/world-modules/src/modules/erc20-puppet/tables/Allowances.sol index c1c3e083ed..8b649d6eb9 100644 --- a/packages/world-modules/src/modules/erc20-puppet/tables/Allowances.sol +++ b/packages/world-modules/src/modules/erc20-puppet/tables/Allowances.sol @@ -215,7 +215,7 @@ library Allowances { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol b/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol index c62353a8e4..8af6dec769 100644 --- a/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol +++ b/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Metadata.sol @@ -609,7 +609,7 @@ library ERC20Metadata { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( uint8 decimals, diff --git a/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Registry.sol b/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Registry.sol index e560cd0430..3d5e4a9a5f 100644 --- a/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Registry.sol +++ b/packages/world-modules/src/modules/erc20-puppet/tables/ERC20Registry.sol @@ -206,7 +206,7 @@ library ERC20Registry { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address tokenAddress) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(tokenAddress); diff --git a/packages/world-modules/src/modules/erc20-puppet/tables/TotalSupply.sol b/packages/world-modules/src/modules/erc20-puppet/tables/TotalSupply.sol index 876013c395..d8a0fdf02f 100644 --- a/packages/world-modules/src/modules/erc20-puppet/tables/TotalSupply.sol +++ b/packages/world-modules/src/modules/erc20-puppet/tables/TotalSupply.sol @@ -191,7 +191,7 @@ library TotalSupply { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 totalSupply) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(totalSupply); diff --git a/packages/world-modules/src/modules/erc721-puppet/ERC721Module.sol b/packages/world-modules/src/modules/erc721-puppet/ERC721Module.sol index 6a297e05cd..b96caea949 100644 --- a/packages/world-modules/src/modules/erc721-puppet/ERC721Module.sol +++ b/packages/world-modules/src/modules/erc721-puppet/ERC721Module.sol @@ -7,6 +7,7 @@ import { Module } from "@latticexyz/world/src/Module.sol"; import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { InstalledModules } from "@latticexyz/world/src/codegen/tables/InstalledModules.sol"; +import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; import { Puppet } from "../puppet/Puppet.sol"; import { createPuppet } from "../puppet/createPuppet.sol"; @@ -57,7 +58,10 @@ contract ERC721Module is Module { // Register the ERC721 tables and system IBaseWorld world = IBaseWorld(_world()); - registrationLibrary.delegatecall(abi.encodeCall(ERC721ModuleRegistrationLibrary.register, (world, namespace))); + (bool success, bytes memory returnData) = registrationLibrary.delegatecall( + abi.encodeCall(ERC721ModuleRegistrationLibrary.register, (world, namespace)) + ); + if (!success) revertWithBytes(returnData); // Initialize the Metadata ERC721Metadata.set(_metadataTableId(namespace), metadata); @@ -72,6 +76,7 @@ contract ERC721Module is Module { // Register the ERC721 in the ERC20Registry if (!ResourceIds.getExists(ERC721_REGISTRY_TABLE_ID)) { + world.registerNamespace(MODULE_NAMESPACE_ID); ERC721Registry.register(ERC721_REGISTRY_TABLE_ID); } ERC721Registry.set(ERC721_REGISTRY_TABLE_ID, namespaceId, puppet); @@ -87,8 +92,13 @@ contract ERC721ModuleRegistrationLibrary { * Register systems and tables for a new ERC721 token in a given namespace */ function register(IBaseWorld world, bytes14 namespace) public { - // Register the tables + // Register the namespace if it doesn't exist yet + ResourceId tokenNamespace = WorldResourceIdLib.encodeNamespace(namespace); + if (!ResourceIds.getExists(tokenNamespace)) { + world.registerNamespace(tokenNamespace); + } + // Register the tables OperatorApproval.register(_operatorApprovalTableId(namespace)); Owners.register(_ownersTableId(namespace)); TokenApproval.register(_tokenApprovalTableId(namespace)); diff --git a/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol b/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol index 3586fa252f..a7f96ef1d4 100644 --- a/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol +++ b/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol @@ -709,7 +709,7 @@ library ERC721Metadata { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( string memory name, diff --git a/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Registry.sol b/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Registry.sol index e563db1aa8..6730fd023e 100644 --- a/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Registry.sol +++ b/packages/world-modules/src/modules/erc721-puppet/tables/ERC721Registry.sol @@ -206,7 +206,7 @@ library ERC721Registry { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address tokenAddress) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(tokenAddress); diff --git a/packages/world-modules/src/modules/erc721-puppet/tables/OperatorApproval.sol b/packages/world-modules/src/modules/erc721-puppet/tables/OperatorApproval.sol index 0cf1a0d775..49fa556e1d 100644 --- a/packages/world-modules/src/modules/erc721-puppet/tables/OperatorApproval.sol +++ b/packages/world-modules/src/modules/erc721-puppet/tables/OperatorApproval.sol @@ -215,7 +215,7 @@ library OperatorApproval { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bool approved) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(approved); diff --git a/packages/world-modules/src/modules/erc721-puppet/tables/Owners.sol b/packages/world-modules/src/modules/erc721-puppet/tables/Owners.sol index 139d8e092a..2dd1521c68 100644 --- a/packages/world-modules/src/modules/erc721-puppet/tables/Owners.sol +++ b/packages/world-modules/src/modules/erc721-puppet/tables/Owners.sol @@ -203,7 +203,7 @@ library Owners { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address owner) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(owner); diff --git a/packages/world-modules/src/modules/erc721-puppet/tables/TokenApproval.sol b/packages/world-modules/src/modules/erc721-puppet/tables/TokenApproval.sol index 908ade8d32..ff748e11b7 100644 --- a/packages/world-modules/src/modules/erc721-puppet/tables/TokenApproval.sol +++ b/packages/world-modules/src/modules/erc721-puppet/tables/TokenApproval.sol @@ -203,7 +203,7 @@ library TokenApproval { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address account) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(account); diff --git a/packages/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol b/packages/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol index 15ef83a978..dce8311f33 100644 --- a/packages/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol +++ b/packages/world-modules/src/modules/erc721-puppet/tables/TokenURI.sol @@ -458,7 +458,7 @@ library TokenURI { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(string memory tokenURI) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/world-modules/src/modules/keysintable/tables/KeysInTable.sol b/packages/world-modules/src/modules/keysintable/tables/KeysInTable.sol index 211c46d853..08e0443c72 100644 --- a/packages/world-modules/src/modules/keysintable/tables/KeysInTable.sol +++ b/packages/world-modules/src/modules/keysintable/tables/KeysInTable.sol @@ -1628,7 +1628,7 @@ library KeysInTable { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( bytes32[] memory keys0, diff --git a/packages/world-modules/src/modules/keysintable/tables/UsedKeysIndex.sol b/packages/world-modules/src/modules/keysintable/tables/UsedKeysIndex.sol index d6b469838e..e3b53fce16 100644 --- a/packages/world-modules/src/modules/keysintable/tables/UsedKeysIndex.sol +++ b/packages/world-modules/src/modules/keysintable/tables/UsedKeysIndex.sol @@ -412,7 +412,7 @@ library UsedKeysIndex { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bool has, uint40 index) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(has, index); diff --git a/packages/world-modules/src/modules/keyswithvalue/KeysWithValueModule.sol b/packages/world-modules/src/modules/keyswithvalue/KeysWithValueModule.sol index 7ca3362bba..cdfdcab433 100644 --- a/packages/world-modules/src/modules/keyswithvalue/KeysWithValueModule.sol +++ b/packages/world-modules/src/modules/keyswithvalue/KeysWithValueModule.sol @@ -2,8 +2,9 @@ pragma solidity >=0.8.21; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { BEFORE_SET_RECORD, BEFORE_SPLICE_STATIC_DATA, AFTER_SPLICE_STATIC_DATA, BEFORE_SPLICE_DYNAMIC_DATA, AFTER_SPLICE_DYNAMIC_DATA, BEFORE_DELETE_RECORD } from "@latticexyz/store/src/storeHookTypes.sol"; -import { Module } from "@latticexyz/world/src/Module.sol"; +import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; +import { Module } from "@latticexyz/world/src/Module.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { InstalledModules } from "@latticexyz/world/src/codegen/index.sol"; @@ -45,16 +46,24 @@ contract KeysWithValueModule is Module { // Extract source table id from args ResourceId sourceTableId = ResourceId.wrap(abi.decode(args, (bytes32))); - ResourceId targetTableSelector = getTargetTableId(MODULE_NAMESPACE, sourceTableId); + ResourceId targetTableId = getTargetTableId(MODULE_NAMESPACE, sourceTableId); IBaseWorld world = IBaseWorld(_world()); + // Register the target namespace if it doesn't exist yet + if (!ResourceIds.getExists(targetTableId.getNamespaceId())) { + (bool registrationSuccess, bytes memory registrationReturnData) = address(world).delegatecall( + abi.encodeCall(world.registerNamespace, (targetTableId.getNamespaceId())) + ); + if (!registrationSuccess) revertWithBytes(registrationReturnData); + } + // Register the target table (bool success, bytes memory returnData) = address(world).delegatecall( abi.encodeCall( world.registerTable, ( - targetTableSelector, + targetTableId, KeysWithValue.getFieldLayout(), KeysWithValue.getKeySchema(), KeysWithValue.getValueSchema(), @@ -64,9 +73,11 @@ contract KeysWithValueModule is Module { ) ); + if (!success) revertWithBytes(returnData); + // Grant the hook access to the target table (success, returnData) = address(world).delegatecall( - abi.encodeCall(world.grantAccess, (targetTableSelector, address(hook))) + abi.encodeCall(world.grantAccess, (targetTableId, address(hook))) ); if (!success) revertWithBytes(returnData); diff --git a/packages/world-modules/src/modules/keyswithvalue/tables/KeysWithValue.sol b/packages/world-modules/src/modules/keyswithvalue/tables/KeysWithValue.sol index c558072f90..8db28b8d01 100644 --- a/packages/world-modules/src/modules/keyswithvalue/tables/KeysWithValue.sol +++ b/packages/world-modules/src/modules/keyswithvalue/tables/KeysWithValue.sol @@ -676,7 +676,7 @@ library KeysWithValue { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bytes32[] memory keysWithValue) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/world-modules/src/modules/puppet/PuppetModule.sol b/packages/world-modules/src/modules/puppet/PuppetModule.sol index ecf4fcfad4..2b5ec19d9c 100644 --- a/packages/world-modules/src/modules/puppet/PuppetModule.sol +++ b/packages/world-modules/src/modules/puppet/PuppetModule.sol @@ -2,13 +2,15 @@ pragma solidity >=0.8.21; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; - import { Module } from "@latticexyz/world/src/Module.sol"; import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; +import { WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; + +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { PuppetFactorySystem } from "./PuppetFactorySystem.sol"; import { PuppetDelegationControl } from "./PuppetDelegationControl.sol"; -import { MODULE_NAME, PUPPET_DELEGATION, PUPPET_FACTORY, PUPPET_TABLE_ID } from "./constants.sol"; +import { MODULE_NAME, PUPPET_DELEGATION, PUPPET_FACTORY, PUPPET_TABLE_ID, NAMESPACE_ID } from "./constants.sol"; import { PuppetRegistry } from "./tables/PuppetRegistry.sol"; @@ -16,6 +18,8 @@ import { PuppetRegistry } from "./tables/PuppetRegistry.sol"; * This module registers tables and delegation control systems required for puppet delegations */ contract PuppetModule is Module { + using WorldResourceIdInstance for ResourceId; + PuppetDelegationControl private immutable puppetDelegationControl = new PuppetDelegationControl(); PuppetFactorySystem private immutable puppetFactorySystem = new PuppetFactorySystem(); @@ -26,11 +30,23 @@ contract PuppetModule is Module { function installRoot(bytes memory) public { IBaseWorld world = IBaseWorld(_world()); + // Register namespace + (bool success, bytes memory returnData) = address(world).delegatecall( + abi.encodeCall(world.registerNamespace, (NAMESPACE_ID)) + ); + if (!success) revertWithBytes(returnData); + // Register table PuppetRegistry.register(PUPPET_TABLE_ID); - // Register system - (bool success, bytes memory returnData) = address(world).delegatecall( + // Register puppet factory + (success, returnData) = address(world).delegatecall( + abi.encodeCall(world.registerSystem, (PUPPET_FACTORY, puppetFactorySystem, true)) + ); + if (!success) revertWithBytes(returnData); + + // Register puppet delegation control + (success, returnData) = address(world).delegatecall( abi.encodeCall(world.registerSystem, (PUPPET_DELEGATION, puppetDelegationControl, true)) ); if (!success) revertWithBytes(returnData); @@ -39,6 +55,9 @@ contract PuppetModule is Module { function install(bytes memory) public { IBaseWorld world = IBaseWorld(_world()); + // Register namespace + world.registerNamespace(NAMESPACE_ID); + // Register table PuppetRegistry.register(PUPPET_TABLE_ID); diff --git a/packages/world-modules/src/modules/puppet/constants.sol b/packages/world-modules/src/modules/puppet/constants.sol index c441dd03b9..88efb01632 100644 --- a/packages/world-modules/src/modules/puppet/constants.sol +++ b/packages/world-modules/src/modules/puppet/constants.sol @@ -3,12 +3,14 @@ pragma solidity >=0.8.21; import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; import { RESOURCE_TABLE } from "@latticexyz/store/src/storeResourceTypes.sol"; -import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; +import { RESOURCE_SYSTEM, RESOURCE_NAMESPACE } from "@latticexyz/world/src/worldResourceTypes.sol"; import { ROOT_NAMESPACE } from "@latticexyz/world/src/constants.sol"; bytes16 constant MODULE_NAME = bytes16("puppet"); bytes14 constant NAMESPACE = bytes14("puppet"); +ResourceId constant NAMESPACE_ID = ResourceId.wrap(bytes32(abi.encodePacked(RESOURCE_NAMESPACE, NAMESPACE))); + ResourceId constant PUPPET_DELEGATION = ResourceId.wrap( bytes32(abi.encodePacked(RESOURCE_SYSTEM, NAMESPACE, bytes16("Delegation"))) ); diff --git a/packages/world-modules/src/modules/puppet/tables/PuppetRegistry.sol b/packages/world-modules/src/modules/puppet/tables/PuppetRegistry.sol index 054b093cbd..8e6b2dfa90 100644 --- a/packages/world-modules/src/modules/puppet/tables/PuppetRegistry.sol +++ b/packages/world-modules/src/modules/puppet/tables/PuppetRegistry.sol @@ -206,7 +206,7 @@ library PuppetRegistry { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address puppet) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(puppet); diff --git a/packages/world-modules/src/modules/std-delegations/tables/CallboundDelegations.sol b/packages/world-modules/src/modules/std-delegations/tables/CallboundDelegations.sol index 1e4b96c541..2801cba1a8 100644 --- a/packages/world-modules/src/modules/std-delegations/tables/CallboundDelegations.sol +++ b/packages/world-modules/src/modules/std-delegations/tables/CallboundDelegations.sol @@ -291,7 +291,7 @@ library CallboundDelegations { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 availableCalls) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(availableCalls); diff --git a/packages/world-modules/src/modules/std-delegations/tables/SystemboundDelegations.sol b/packages/world-modules/src/modules/std-delegations/tables/SystemboundDelegations.sol index 49fd116cc1..19d1c6f9c2 100644 --- a/packages/world-modules/src/modules/std-delegations/tables/SystemboundDelegations.sol +++ b/packages/world-modules/src/modules/std-delegations/tables/SystemboundDelegations.sol @@ -261,7 +261,7 @@ library SystemboundDelegations { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 availableCalls) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(availableCalls); diff --git a/packages/world-modules/src/modules/std-delegations/tables/TimeboundDelegations.sol b/packages/world-modules/src/modules/std-delegations/tables/TimeboundDelegations.sol index 48e4445f61..e75d018947 100644 --- a/packages/world-modules/src/modules/std-delegations/tables/TimeboundDelegations.sol +++ b/packages/world-modules/src/modules/std-delegations/tables/TimeboundDelegations.sol @@ -220,7 +220,7 @@ library TimeboundDelegations { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 maxTimestamp) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(maxTimestamp); diff --git a/packages/world-modules/src/modules/tokens/tables/Balances.sol b/packages/world-modules/src/modules/tokens/tables/Balances.sol index bad1af5c6d..6af8925cf8 100644 --- a/packages/world-modules/src/modules/tokens/tables/Balances.sol +++ b/packages/world-modules/src/modules/tokens/tables/Balances.sol @@ -203,7 +203,7 @@ library Balances { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/packages/world-modules/src/modules/uniqueentity/UniqueEntityModule.sol b/packages/world-modules/src/modules/uniqueentity/UniqueEntityModule.sol index 36ddb01e8a..1ba3c0f3f4 100644 --- a/packages/world-modules/src/modules/uniqueentity/UniqueEntityModule.sol +++ b/packages/world-modules/src/modules/uniqueentity/UniqueEntityModule.sol @@ -11,7 +11,7 @@ import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol"; import { UniqueEntity } from "./tables/UniqueEntity.sol"; import { UniqueEntitySystem } from "./UniqueEntitySystem.sol"; -import { MODULE_NAME, TABLE_ID, SYSTEM_ID } from "./constants.sol"; +import { MODULE_NAME, TABLE_ID, SYSTEM_ID, NAMESPACE_ID } from "./constants.sol"; /** * This module creates a table that stores a nonce, and @@ -33,11 +33,17 @@ contract UniqueEntityModule is Module { IBaseWorld world = IBaseWorld(_world()); + // Register namespace + (bool success, bytes memory data) = address(world).delegatecall( + abi.encodeCall(world.registerNamespace, (NAMESPACE_ID)) + ); + if (!success) revertWithBytes(data); + // Register table UniqueEntity._register(TABLE_ID); // Register system - (bool success, bytes memory data) = address(world).delegatecall( + (success, data) = address(world).delegatecall( abi.encodeCall(world.registerSystem, (SYSTEM_ID, uniqueEntitySystem, true)) ); if (!success) revertWithBytes(data); @@ -56,6 +62,9 @@ contract UniqueEntityModule is Module { IBaseWorld world = IBaseWorld(_world()); + // Register namespace + world.registerNamespace(NAMESPACE_ID); + // Register table UniqueEntity.register(TABLE_ID); diff --git a/packages/world-modules/src/modules/uniqueentity/constants.sol b/packages/world-modules/src/modules/uniqueentity/constants.sol index 07c4691423..ebefa29b7b 100644 --- a/packages/world-modules/src/modules/uniqueentity/constants.sol +++ b/packages/world-modules/src/modules/uniqueentity/constants.sol @@ -2,12 +2,13 @@ pragma solidity >=0.8.21; import { ResourceId } from "@latticexyz/world/src/WorldResourceId.sol"; -import { RESOURCE_TABLE, RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; +import { RESOURCE_TABLE, RESOURCE_SYSTEM, RESOURCE_NAMESPACE } from "@latticexyz/world/src/worldResourceTypes.sol"; bytes14 constant NAMESPACE = bytes14("uniqueEntity"); bytes16 constant MODULE_NAME = bytes16("uniqueEntity"); bytes16 constant SYSTEM_NAME = bytes16("system"); bytes16 constant TABLE_NAME = bytes16("table"); +ResourceId constant NAMESPACE_ID = ResourceId.wrap(bytes32(abi.encodePacked(RESOURCE_NAMESPACE, NAMESPACE))); ResourceId constant TABLE_ID = ResourceId.wrap(bytes32(abi.encodePacked(RESOURCE_TABLE, NAMESPACE, TABLE_NAME))); ResourceId constant SYSTEM_ID = ResourceId.wrap((bytes32(abi.encodePacked(RESOURCE_SYSTEM, NAMESPACE, SYSTEM_NAME)))); diff --git a/packages/world-modules/src/modules/uniqueentity/tables/UniqueEntity.sol b/packages/world-modules/src/modules/uniqueentity/tables/UniqueEntity.sol index fe577682d2..7350853225 100644 --- a/packages/world-modules/src/modules/uniqueentity/tables/UniqueEntity.sol +++ b/packages/world-modules/src/modules/uniqueentity/tables/UniqueEntity.sol @@ -245,7 +245,7 @@ library UniqueEntity { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/packages/world-modules/test/PuppetModule.t.sol b/packages/world-modules/test/PuppetModule.t.sol index 7e29308620..5025d49866 100644 --- a/packages/world-modules/test/PuppetModule.t.sol +++ b/packages/world-modules/test/PuppetModule.t.sol @@ -49,9 +49,25 @@ contract PuppetModuleTest is Test, GasReporter { function setUp() public { world = IBaseWorld(address(new World())); world.initialize(new CoreModule()); + } + + function _setupPuppet() internal { world.installModule(new PuppetModule(), new bytes(0)); + // Register a new namespace and system + world.registerNamespace(systemId.getNamespaceId()); + PuppetTestSystem system = new PuppetTestSystem(); + world.registerSystem(systemId, system, true); + + // Connect the puppet + puppet = PuppetTestSystem(createPuppet(world, systemId)); + } + + function _setupRootPuppet() internal { + world.installRootModule(new PuppetModule(), new bytes(0)); + // Register a new system + world.registerNamespace(systemId.getNamespaceId()); PuppetTestSystem system = new PuppetTestSystem(); world.registerSystem(systemId, system, true); @@ -60,6 +76,8 @@ contract PuppetModuleTest is Test, GasReporter { } function testEmitOnPuppet() public { + _setupPuppet(); + vm.expectEmit(true, true, true, true); emit Hello("hello world"); string memory result = puppet.echoAndEmit("hello world"); @@ -67,6 +85,23 @@ contract PuppetModuleTest is Test, GasReporter { } function testMsgSender() public { + _setupPuppet(); + + assertEq(puppet.msgSender(), address(this)); + } + + function testEmitOnRootPuppet() public { + _setupRootPuppet(); + + vm.expectEmit(true, true, true, true); + emit Hello("hello world"); + string memory result = puppet.echoAndEmit("hello world"); + assertEq(result, "hello world"); + } + + function testMsgSenderRootPuppet() public { + _setupRootPuppet(); + assertEq(puppet.msgSender(), address(this)); } } diff --git a/packages/world-modules/test/StandardDelegationsModule.t.sol b/packages/world-modules/test/StandardDelegationsModule.t.sol index fd7b4a65de..adfc7d613e 100644 --- a/packages/world-modules/test/StandardDelegationsModule.t.sol +++ b/packages/world-modules/test/StandardDelegationsModule.t.sol @@ -25,6 +25,7 @@ import { CALLBOUND_DELEGATION, SYSTEMBOUND_DELEGATION, TIMEBOUND_DELEGATION } fr import { WorldTestSystem, WorldTestSystemReturn } from "@latticexyz/world/test/World.t.sol"; contract StandardDelegationsModuleTest is Test, GasReporter { + using WorldResourceIdInstance for ResourceId; IBaseWorld private world; ResourceId private systemId = WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: "namespace", name: "testSystem" }); @@ -38,6 +39,7 @@ contract StandardDelegationsModuleTest is Test, GasReporter { // Register a new system WorldTestSystem system = new WorldTestSystem(); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); } diff --git a/packages/world-modules/test/SystemSwitch.t.sol b/packages/world-modules/test/SystemSwitch.t.sol index cce9879230..6f8cf04f76 100644 --- a/packages/world-modules/test/SystemSwitch.t.sol +++ b/packages/world-modules/test/SystemSwitch.t.sol @@ -8,7 +8,7 @@ import { System } from "@latticexyz/world/src/System.sol"; import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; import { World } from "@latticexyz/world/src/World.sol"; import { CoreModule } from "@latticexyz/world/src/modules/core/CoreModule.sol"; -import { ResourceId, WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol"; +import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol"; import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol"; import { ROOT_NAMESPACE } from "@latticexyz/world/src/constants.sol"; import { SystemSwitch } from "../src/utils/SystemSwitch.sol"; @@ -38,6 +38,8 @@ contract EchoSystem is System { address constant caller = address(4232); contract SystemSwitchTest is Test, GasReporter { + using WorldResourceIdInstance for ResourceId; + IBaseWorld world; EchoSystem systemA; @@ -68,6 +70,10 @@ contract SystemSwitchTest is Test, GasReporter { rootSystemAId = WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: ROOT_NAMESPACE, name: "systemA" }); rootSystemBId = WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: ROOT_NAMESPACE, name: "systemB" }); + // Register namespaces + world.registerNamespace(systemAId.getNamespaceId()); + world.registerNamespace(systemBId.getNamespaceId()); + // Register systems world.registerSystem(systemAId, systemA, true); world.registerSystem(systemBId, systemB, true); diff --git a/packages/world-modules/test/UniqueEntityModule.t.sol b/packages/world-modules/test/UniqueEntityModule.t.sol index 5979a67435..9808e28743 100644 --- a/packages/world-modules/test/UniqueEntityModule.t.sol +++ b/packages/world-modules/test/UniqueEntityModule.t.sol @@ -126,6 +126,7 @@ contract UniqueEntityModuleTest is Test, GasReporter { namespace: "somens", name: "echoUniqueEntity" }); + world.registerNamespace(uniqueEntityTestSystemId.getNamespaceId()); world.registerSystem(uniqueEntityTestSystemId, uniqueEntityTestSystem, true); // Execute `getUniqueEntity` from the context of a World diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index caf93441b1..5c87427efd 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -69,7 +69,7 @@ "file": "test/World.t.sol", "test": "testCallFromUnlimitedDelegation", "name": "register an unlimited delegation", - "gasUsed": 47623 + "gasUsed": 47651 }, { "file": "test/World.t.sol", @@ -81,79 +81,79 @@ "file": "test/World.t.sol", "test": "testDeleteRecord", "name": "Delete record", - "gasUsed": 9837 + "gasUsed": 9846 }, { "file": "test/World.t.sol", "test": "testPushToDynamicField", "name": "Push data to the table", - "gasUsed": 85811 + "gasUsed": 85842 }, { "file": "test/World.t.sol", "test": "testRegisterFunctionSelector", "name": "Register a function selector", - "gasUsed": 83149 + "gasUsed": 83156 }, { "file": "test/World.t.sol", "test": "testRegisterNamespace", "name": "Register a new namespace", - "gasUsed": 120887 + "gasUsed": 120962 }, { "file": "test/World.t.sol", "test": "testRegisterRootFunctionSelector", "name": "Register a root function selector", - "gasUsed": 80433 + "gasUsed": 80462 }, { "file": "test/World.t.sol", "test": "testRegisterSystem", "name": "register a system", - "gasUsed": 164238 + "gasUsed": 164349 }, { "file": "test/World.t.sol", "test": "testRegisterTable", "name": "Register a new table in the namespace", - "gasUsed": 636447 + "gasUsed": 528422 }, { "file": "test/World.t.sol", "test": "testSetField", "name": "Write data to a table field", - "gasUsed": 36881 + "gasUsed": 36912 }, { "file": "test/World.t.sol", "test": "testSetRecord", "name": "Write data to the table", - "gasUsed": 39039 + "gasUsed": 39056 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromDynamicField", "name": "pop 1 address (cold)", - "gasUsed": 25602 + "gasUsed": 25627 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testPopFromDynamicField", "name": "pop 1 address (warm)", - "gasUsed": 12748 + "gasUsed": 12773 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testSpliceDynamicData", "name": "update in field 1 item (cold)", - "gasUsed": 25953 + "gasUsed": 25990 }, { "file": "test/WorldDynamicUpdate.t.sol", "test": "testSpliceDynamicData", "name": "update in field 1 item (warm)", - "gasUsed": 13154 + "gasUsed": 13191 }, { "file": "test/WorldResourceId.t.sol", diff --git a/packages/world/src/AccessControl.sol b/packages/world/src/AccessControl.sol index 988fd1b7f1..0933bec75c 100644 --- a/packages/world/src/AccessControl.sol +++ b/packages/world/src/AccessControl.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; +import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; + import { ResourceId, WorldResourceIdInstance } from "./WorldResourceId.sol"; import { IWorldErrors } from "./IWorldErrors.sol"; @@ -50,4 +52,15 @@ library AccessControl { revert IWorldErrors.World_AccessDenied(resourceId.toString(), caller); } } + + /** + * @notice Check for existence of the given resource ID. + * @dev Reverts with IWorldErrors.World_ResourceNotFound if the resource does not exist. + * @param resourceId The resource ID to check existence for. + */ + function requireExistence(ResourceId resourceId) internal view { + if (!ResourceIds._getExists(resourceId)) { + revert IWorldErrors.World_ResourceNotFound(resourceId, resourceId.toString()); + } + } } diff --git a/packages/world/src/WorldResourceId.sol b/packages/world/src/WorldResourceId.sol index c609621f60..d085428251 100644 --- a/packages/world/src/WorldResourceId.sol +++ b/packages/world/src/WorldResourceId.sol @@ -7,8 +7,8 @@ import { ResourceId, ResourceIdInstance, TYPE_BITS } from "@latticexyz/store/src import { ROOT_NAMESPACE, ROOT_NAME } from "./constants.sol"; import { RESOURCE_NAMESPACE, MASK_RESOURCE_NAMESPACE } from "./worldResourceTypes.sol"; -uint256 constant NAMESPACE_BITS = 14 * 8; -uint256 constant NAME_BITS = 16 * 8; +uint256 constant NAMESPACE_BITS = 14 * 8; // 14 bytes * 8 bits per byte +uint256 constant NAME_BITS = 16 * 8; // 16 bytes * 8 bits per byte bytes16 constant ROOT_NAMESPACE_STRING = bytes16("ROOT_NAMESPACE"); bytes16 constant ROOT_NAME_STRING = bytes16("ROOT_NAME"); diff --git a/packages/world/src/codegen/tables/Balances.sol b/packages/world/src/codegen/tables/Balances.sol index 96d0fe791e..071750373f 100644 --- a/packages/world/src/codegen/tables/Balances.sol +++ b/packages/world/src/codegen/tables/Balances.sol @@ -211,7 +211,7 @@ library Balances { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint256 balance) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(balance); diff --git a/packages/world/src/codegen/tables/FunctionSelectors.sol b/packages/world/src/codegen/tables/FunctionSelectors.sol index e58c224c9a..cea95028bc 100644 --- a/packages/world/src/codegen/tables/FunctionSelectors.sol +++ b/packages/world/src/codegen/tables/FunctionSelectors.sol @@ -296,7 +296,7 @@ library FunctionSelectors { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( ResourceId systemId, diff --git a/packages/world/src/codegen/tables/FunctionSignatures.sol b/packages/world/src/codegen/tables/FunctionSignatures.sol index 45f8efd487..9f4e5b4345 100644 --- a/packages/world/src/codegen/tables/FunctionSignatures.sol +++ b/packages/world/src/codegen/tables/FunctionSignatures.sol @@ -192,7 +192,7 @@ library FunctionSignatures { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(string memory functionSignature) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/world/src/codegen/tables/InstalledModules.sol b/packages/world/src/codegen/tables/InstalledModules.sol index ff16296f5d..24a69c1386 100644 --- a/packages/world/src/codegen/tables/InstalledModules.sol +++ b/packages/world/src/codegen/tables/InstalledModules.sol @@ -220,7 +220,7 @@ library InstalledModules { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address moduleAddress) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(moduleAddress); diff --git a/packages/world/src/codegen/tables/NamespaceDelegationControl.sol b/packages/world/src/codegen/tables/NamespaceDelegationControl.sol index 5c13ef1dab..4e92b94f08 100644 --- a/packages/world/src/codegen/tables/NamespaceDelegationControl.sol +++ b/packages/world/src/codegen/tables/NamespaceDelegationControl.sol @@ -235,7 +235,7 @@ library NamespaceDelegationControl { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(ResourceId delegationControlId) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(delegationControlId); diff --git a/packages/world/src/codegen/tables/NamespaceOwner.sol b/packages/world/src/codegen/tables/NamespaceOwner.sol index 84c23e729f..2c29dff8f8 100644 --- a/packages/world/src/codegen/tables/NamespaceOwner.sol +++ b/packages/world/src/codegen/tables/NamespaceOwner.sol @@ -211,7 +211,7 @@ library NamespaceOwner { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address owner) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(owner); diff --git a/packages/world/src/codegen/tables/ResourceAccess.sol b/packages/world/src/codegen/tables/ResourceAccess.sol index c7a4064708..755fd757d6 100644 --- a/packages/world/src/codegen/tables/ResourceAccess.sol +++ b/packages/world/src/codegen/tables/ResourceAccess.sol @@ -223,7 +223,7 @@ library ResourceAccess { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bool access) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(access); diff --git a/packages/world/src/codegen/tables/SystemHooks.sol b/packages/world/src/codegen/tables/SystemHooks.sol index 01d11b7bbe..fa13166bf8 100644 --- a/packages/world/src/codegen/tables/SystemHooks.sol +++ b/packages/world/src/codegen/tables/SystemHooks.sol @@ -462,7 +462,7 @@ library SystemHooks { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bytes21[] memory value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/world/src/codegen/tables/SystemRegistry.sol b/packages/world/src/codegen/tables/SystemRegistry.sol index 150d77a536..af6e6707c2 100644 --- a/packages/world/src/codegen/tables/SystemRegistry.sol +++ b/packages/world/src/codegen/tables/SystemRegistry.sol @@ -211,7 +211,7 @@ library SystemRegistry { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(ResourceId systemId) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(systemId); diff --git a/packages/world/src/codegen/tables/Systems.sol b/packages/world/src/codegen/tables/Systems.sol index 09dc0c4b3f..bc10a9a446 100644 --- a/packages/world/src/codegen/tables/Systems.sol +++ b/packages/world/src/codegen/tables/Systems.sol @@ -296,7 +296,7 @@ library Systems { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address system, bool publicAccess) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(system, publicAccess); diff --git a/packages/world/src/codegen/tables/UserDelegationControl.sol b/packages/world/src/codegen/tables/UserDelegationControl.sol index 6e075b4211..4c2de3caa6 100644 --- a/packages/world/src/codegen/tables/UserDelegationControl.sol +++ b/packages/world/src/codegen/tables/UserDelegationControl.sol @@ -253,7 +253,7 @@ library UserDelegationControl { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(ResourceId delegationControlId) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(delegationControlId); diff --git a/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol b/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol index 085e98db7f..53b7c88fc0 100644 --- a/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol +++ b/packages/world/src/modules/core/implementations/BalanceTransferSystem.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.21; import { ResourceId, ResourceIdInstance } from "@latticexyz/store/src/ResourceId.sol"; +import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; import { System } from "../../../System.sol"; import { revertWithBytes } from "../../../revertWithBytes.sol"; @@ -37,6 +38,9 @@ contract BalanceTransferSystem is System, IWorldErrors { revert World_InvalidResourceType(RESOURCE_NAMESPACE, toNamespaceId, toNamespaceId.toString()); } + // Require the namespace to exist + AccessControl.requireExistence(toNamespaceId); + // Require caller to have access to the namespace AccessControl.requireAccess(fromNamespaceId, _msgSender()); diff --git a/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol b/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol index 1df3ca8a96..c380ce0cdd 100644 --- a/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol +++ b/packages/world/src/modules/core/implementations/StoreRegistrationSystem.sol @@ -5,7 +5,6 @@ import { IStoreHook, STORE_HOOK_INTERFACE_ID } from "@latticexyz/store/src/IStor import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; import { Schema } from "@latticexyz/store/src/Schema.sol"; -import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; import { System } from "../../../System.sol"; import { ResourceId, WorldResourceIdInstance } from "../../../WorldResourceId.sol"; @@ -14,8 +13,6 @@ import { AccessControl } from "../../../AccessControl.sol"; import { requireInterface } from "../../../requireInterface.sol"; import { revertWithBytes } from "../../../revertWithBytes.sol"; -import { Systems } from "../../../codegen/tables/Systems.sol"; - import { IWorldErrors } from "../../../IWorldErrors.sol"; import { CORE_SYSTEM_ID } from "../constants.sol"; @@ -49,23 +46,16 @@ contract StoreRegistrationSystem is System, IWorldErrors { string[] calldata fieldNames ) public virtual { // Require the name to not be the namespace's root name - if (tableId.getName() == ROOT_NAME) revert World_InvalidResourceId(tableId, tableId.toString()); - - // If the namespace doesn't exist yet, register it - ResourceId namespaceId = tableId.getNamespaceId(); - if (!ResourceIds._getExists(namespaceId)) { - // Since this is a root system, we're in the context of the World contract already, - // so we can use delegatecall to register the namespace - address coreSystemAddress = Systems._getSystem(CORE_SYSTEM_ID); - (bool success, bytes memory data) = coreSystemAddress.delegatecall( - abi.encodeCall(WorldRegistrationSystem.registerNamespace, (namespaceId)) - ); - if (!success) revertWithBytes(data); - } else { - // otherwise require caller to own the namespace - AccessControl.requireOwner(namespaceId, _msgSender()); + if (tableId.getName() == ROOT_NAME) { + revert World_InvalidResourceId(tableId, tableId.toString()); } + // Require the table's namespace to exist + AccessControl.requireExistence(tableId.getNamespaceId()); + + // Require the caller to own the table's namespace + AccessControl.requireOwner(tableId, _msgSender()); + // Register the table StoreCore.registerTable(tableId, fieldLayout, keySchema, valueSchema, keyNames, fieldNames); } diff --git a/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol b/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol index 21025a33c5..11cd866970 100644 --- a/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol +++ b/packages/world/src/modules/core/implementations/WorldRegistrationSystem.sol @@ -113,6 +113,13 @@ contract WorldRegistrationSystem is System, IWorldErrors { revert World_InvalidResourceType(RESOURCE_SYSTEM, systemId, systemId.toString()); } + // Require the system's namespace to exist + ResourceId namespaceId = systemId.getNamespaceId(); + AccessControl.requireExistence(namespaceId); + + // Require the caller to own the namespace + AccessControl.requireOwner(namespaceId, _msgSender()); + // Require the provided address to implement the WorldContextConsumer interface requireInterface(address(system), WORLD_CONTEXT_CONSUMER_INTERFACE_ID); @@ -127,15 +134,6 @@ contract WorldRegistrationSystem is System, IWorldErrors { revert World_SystemAlreadyExists(address(system)); } - // If the namespace doesn't exist yet, register it - ResourceId namespaceId = systemId.getNamespaceId(); - if (!ResourceIds._getExists(namespaceId)) { - registerNamespace(namespaceId); - } else { - // otherwise require caller to own the namespace - AccessControl.requireOwner(namespaceId, _msgSender()); - } - // Check if a system already exists at this system ID address existingSystem = Systems._getSystem(systemId); diff --git a/packages/world/test/BatchCall.t.sol b/packages/world/test/BatchCall.t.sol index 099f32ae59..e90b461f9f 100644 --- a/packages/world/test/BatchCall.t.sol +++ b/packages/world/test/BatchCall.t.sol @@ -8,7 +8,7 @@ import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { World } from "../src/World.sol"; import { System } from "../src/System.sol"; import { UNLIMITED_DELEGATION } from "../src/constants.sol"; -import { ResourceId, WorldResourceIdLib } from "../src/WorldResourceId.sol"; +import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "../src/WorldResourceId.sol"; import { RESOURCE_SYSTEM } from "../src/worldResourceTypes.sol"; import { IWorldErrors } from "../src/IWorldErrors.sol"; @@ -47,6 +47,8 @@ contract TestSystem is System { } contract BatchCallTest is Test, GasReporter { + using WorldResourceIdInstance for ResourceId; + IBaseWorld world; bytes14 namespace = "namespace"; bytes16 name = "testSystem"; @@ -56,6 +58,7 @@ contract BatchCallTest is Test, GasReporter { function setUp() public { world = IBaseWorld(address(new World())); world.initialize(new CoreModule()); + world.registerNamespace(systemId.getNamespaceId()); } function testBatchCall() public { diff --git a/packages/world/test/Utils.t.sol b/packages/world/test/Utils.t.sol index b0458c9613..b2660f5210 100644 --- a/packages/world/test/Utils.t.sol +++ b/packages/world/test/Utils.t.sol @@ -9,6 +9,8 @@ import { World } from "../src/World.sol"; import { IBaseWorld } from "../src/codegen/interfaces/IBaseWorld.sol"; import { ResourceId, WorldResourceIdLib, WorldResourceIdInstance } from "../src/WorldResourceId.sol"; import { RESOURCE_SYSTEM } from "../src/worldResourceTypes.sol"; +import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { CoreModule } from "../src/modules/core/CoreModule.sol"; @@ -27,12 +29,17 @@ contract UtilsTest is Test { function setUp() public { world = IBaseWorld(address(new World())); world.initialize(new CoreModule()); + StoreSwitch.setStoreAddress(address(world)); } function _registerAndGetNamespace(bytes14 namespace) internal returns (bytes16 returnedNamespace) { UtilsTestSystem testSystem = new UtilsTestSystem(); bytes16 name = "testSystem"; ResourceId systemId = WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: name }); + ResourceId namespaceId = WorldResourceIdLib.encodeNamespace(namespace); + if (!ResourceIds.getExists(namespaceId)) { + world.registerNamespace(namespaceId); + } world.registerSystem(systemId, testSystem, true); bytes memory data = world.call(systemId, abi.encodeCall(UtilsTestSystem.systemNamespace, ())); diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 678e276c8b..c6651b360e 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -308,6 +308,7 @@ contract WorldTest is Test, GasReporter { name: "testSystem" }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, false); bytes memory result = world.call(systemId, abi.encodeCall(WorldTestSystem.getStoreAddress, ())); @@ -431,6 +432,7 @@ contract WorldTest is Test, GasReporter { fieldNames[0] = "value1"; fieldNames[1] = "value2"; fieldNames[2] = "value3"; + world.registerNamespace(tableId.getNamespaceId()); startGasReport("Register a new table in the namespace"); world.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, keyNames, fieldNames); @@ -460,7 +462,7 @@ contract WorldTest is Test, GasReporter { namespace: namespace, name: "otherTable" }); - _expectAccessDenied(address(0x01), namespace, "", RESOURCE_NAMESPACE); + _expectAccessDenied(address(0x01), namespace, "otherTable", RESOURCE_TABLE); world.registerTable(otherTableId, fieldLayout, defaultKeySchema, valueSchema, keyNames, fieldNames); // Expect the World to not be allowed to call registerTable via an external call @@ -505,15 +507,22 @@ contract WorldTest is Test, GasReporter { // Expect the system to have access to its own namespace assertTrue(ResourceAccess.get({ resourceId: namespaceId, caller: address(system) })); - ResourceId newNamespaceId = WorldResourceIdLib.encodeNamespace("newNamespace"); - // Expect the namespace to be created if it doesn't exist yet - assertEq(NamespaceOwner.get(newNamespaceId), address(0)); - world.registerSystem( - WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: "newNamespace", name: "testSystem" }), - new System(), - false + // Expect the registration to fail if the namespace does not exist yet + System newSystem = new System(); + ResourceId invalidNamespaceSystemId = WorldResourceIdLib.encode({ + typeId: RESOURCE_SYSTEM, + namespace: "newNamespace", + name: "testSystem" + }); + assertEq(NamespaceOwner.get(invalidNamespaceSystemId.getNamespaceId()), address(0)); + vm.expectRevert( + abi.encodeWithSelector( + IWorldErrors.World_ResourceNotFound.selector, + invalidNamespaceSystemId.getNamespaceId(), + invalidNamespaceSystemId.getNamespaceId().toString() + ) ); - assertEq(NamespaceOwner.get(newNamespaceId), address(this)); + world.registerSystem(invalidNamespaceSystemId, newSystem, false); // Expect an error when registering an existing system at a new system ID vm.expectRevert(abi.encodeWithSelector(IWorldErrors.World_SystemAlreadyExists.selector, address(system))); @@ -527,7 +536,6 @@ contract WorldTest is Test, GasReporter { world.registerSystem(systemId, system, true); // Expect an error when registering a system at an existing resource ID of a different type - System newSystem = new System(); ResourceId tableId = WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: "", name: "testTable" }); world.registerTable( tableId, @@ -576,7 +584,7 @@ contract WorldTest is Test, GasReporter { ) ); world.registerSystem( - WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: "someNamespace", name: "invalidSystem" }), + WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: "", name: "invalidSystem" }), System(address(world)), true ); @@ -591,6 +599,7 @@ contract WorldTest is Test, GasReporter { namespace: namespace, name: systemName }); + world.registerNamespace(systemId.getNamespaceId()); // Register a system System oldSystem = new System(); @@ -631,6 +640,10 @@ contract WorldTest is Test, GasReporter { } function testInvalidIds() public { + // Register the namespaces + world.registerNamespace(WorldResourceIdLib.encodeNamespace("namespace")); + world.registerNamespace(WorldResourceIdLib.encodeNamespace("namespace2")); + // Register a new table ResourceId tableId = WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: "namespace", name: "name" }); world.registerTable( @@ -706,6 +719,7 @@ contract WorldTest is Test, GasReporter { namespace: "testSetRecord", name: "testTable" }); + world.registerNamespace(tableId.getNamespaceId()); // Register a new table world.registerTable( tableId, @@ -741,6 +755,7 @@ contract WorldTest is Test, GasReporter { ResourceId tableId = WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: name }); FieldLayout fieldLayout = Bool.getFieldLayout(); Schema valueSchema = Bool.getValueSchema(); + world.registerNamespace(tableId.getNamespaceId()); // Register a new table world.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](1)); @@ -772,6 +787,7 @@ contract WorldTest is Test, GasReporter { Schema valueSchema = AddressArray.getValueSchema(); // Register a new table + world.registerNamespace(tableId.getNamespaceId()); world.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](1)); // Create data @@ -817,6 +833,7 @@ contract WorldTest is Test, GasReporter { Schema valueSchema = Bool.getValueSchema(); // Register a new table + world.registerNamespace(tableId.getNamespaceId()); world.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](1)); // Write data to the table and expect it to be written @@ -855,6 +872,7 @@ contract WorldTest is Test, GasReporter { namespace: "namespace", name: "testSystem" }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, false); // Call a system function without arguments via the World @@ -933,6 +951,7 @@ contract WorldTest is Test, GasReporter { namespace: "namespace", name: "testSystem" }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); address caller = address(1); @@ -954,6 +973,7 @@ contract WorldTest is Test, GasReporter { namespace: "namespace", name: "testSystem" }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); // Register an unlimited delegation @@ -983,6 +1003,7 @@ contract WorldTest is Test, GasReporter { namespace: "namespace", name: "testSystem" }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); // Expect a revert when attempting to perform a call on behalf of an address that doesn't have a delegation @@ -1005,6 +1026,7 @@ contract WorldTest is Test, GasReporter { namespace: "namespace", name: "testSystem" }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); // Register a limited delegation @@ -1022,6 +1044,7 @@ contract WorldTest is Test, GasReporter { namespace: "namespace", name: "testSystem" }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); // Register a delegation control mock system @@ -1031,6 +1054,7 @@ contract WorldTest is Test, GasReporter { namespace: "delegation", name: "mock" }); + world.registerNamespace(delegationControlMockId.getNamespaceId()); world.registerSystem(delegationControlMockId, delegationControlMock, true); address delegator = address(1); @@ -1224,6 +1248,7 @@ contract WorldTest is Test, GasReporter { // Register a new system WorldTestSystem system = new WorldTestSystem(); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); // Expect the registration to fail if the contract does not implement the system hook interface @@ -1268,6 +1293,7 @@ contract WorldTest is Test, GasReporter { }); // Register a new system + world.registerNamespace(systemId.getNamespaceId()); WorldTestSystem system = new WorldTestSystem(); world.registerSystem(systemId, system, false); @@ -1309,6 +1335,7 @@ contract WorldTest is Test, GasReporter { name: "testTable" }); // Register a new table + world.registerNamespace(tableId.getNamespaceId()); world.registerTable( tableId, Bool.getFieldLayout(), @@ -1337,6 +1364,7 @@ contract WorldTest is Test, GasReporter { name: "testTable" }); // Register a new table + world.registerNamespace(tableId.getNamespaceId()); world.registerTable( tableId, Bool.getFieldLayout(), @@ -1382,6 +1410,7 @@ contract WorldTest is Test, GasReporter { }); // Register a new non-root system WorldTestSystem system = new WorldTestSystem(); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, false); // Call the sysyem @@ -1397,6 +1426,7 @@ contract WorldTest is Test, GasReporter { // Register a new system WorldTestSystem system = new WorldTestSystem(); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); startGasReport("Register a function selector"); @@ -1428,6 +1458,7 @@ contract WorldTest is Test, GasReporter { // Register a new system WorldTestSystem system = new WorldTestSystem(); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); string memory worldFunc = "testSelector()"; @@ -1495,6 +1526,7 @@ contract WorldTest is Test, GasReporter { bytes16 name = "testSystem"; ResourceId systemId = WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: name }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); world.registerRootFunctionSelector(systemId, "receiveEther()", WorldTestSystem.receiveEther.selector); @@ -1521,6 +1553,7 @@ contract WorldTest is Test, GasReporter { bytes14 namespace = "noroot"; bytes16 name = "testSystem"; ResourceId systemId = WorldResourceIdLib.encode({ typeId: RESOURCE_SYSTEM, namespace: namespace, name: name }); + world.registerNamespace(systemId.getNamespaceId()); world.registerSystem(systemId, system, true); world.registerRootFunctionSelector(systemId, "msgSender()", WorldTestSystem.msgSender.selector); diff --git a/packages/world/test/WorldBalance.t.sol b/packages/world/test/WorldBalance.t.sol index c43c09b0dc..3e92b2b4bf 100644 --- a/packages/world/test/WorldBalance.t.sol +++ b/packages/world/test/WorldBalance.t.sol @@ -39,6 +39,8 @@ contract WorldBalanceTest is Test, GasReporter { world.initialize(new CoreModule()); StoreSwitch.setStoreAddress(address(world)); + world.registerNamespace(namespaceId); + world.registerSystem(rootSystemId, rootSystem, true); world.registerSystem(nonRootSystemId, nonRootSystem, true); @@ -274,6 +276,32 @@ contract WorldBalanceTest is Test, GasReporter { assertEq(Balances.get(invalidNamespace), 0); } + function testTransferBalanceToNamespaceRevertResourceNotFound() public { + bytes14 toNamespace = "not_registered"; + ResourceId toNamespaceId = WorldResourceIdLib.encodeNamespace(toNamespace); + + uint256 value = 1 ether; + + // Expect the root namespace to have no balance + assertEq(Balances.get(ROOT_NAMESPACE_ID), 0); + + // Send balance to root namespace + vm.deal(caller, value); + vm.prank(caller); + (bool success, bytes memory data) = address(world).call{ value: value }(""); + assertTrue(success); + assertEq(data.length, 0); + + // Expect the root namespace to have the value as balance + assertEq(Balances.get(ROOT_NAMESPACE_ID), value); + + // Expect revert when attempting to transfer to a non-existent namespace + vm.expectRevert( + abi.encodeWithSelector(IWorldErrors.World_ResourceNotFound.selector, toNamespaceId, toNamespaceId.toString()) + ); + world.transferBalanceToNamespace(ROOT_NAMESPACE_ID, toNamespaceId, value); + } + function testTransferBalanceToAddress() public { uint256 value = 1 ether; diff --git a/packages/world/test/WorldDynamicUpdate.t.sol b/packages/world/test/WorldDynamicUpdate.t.sol index f8680e6fa0..a70d6e7b5e 100644 --- a/packages/world/test/WorldDynamicUpdate.t.sol +++ b/packages/world/test/WorldDynamicUpdate.t.sol @@ -64,6 +64,7 @@ contract UpdateInDynamicFieldTest is Test, GasReporter { tableId = WorldResourceIdLib.encode({ typeId: RESOURCE_TABLE, namespace: namespace, name: name }); // Register a new table + world.registerNamespace(tableId.getNamespaceId()); world.registerTable(tableId, fieldLayout, defaultKeySchema, valueSchema, new string[](1), new string[](1)); // Create data diff --git a/packages/world/test/codegen/tables/AddressArray.sol b/packages/world/test/codegen/tables/AddressArray.sol index 2fd41c8983..08aad51d8a 100644 --- a/packages/world/test/codegen/tables/AddressArray.sol +++ b/packages/world/test/codegen/tables/AddressArray.sol @@ -454,7 +454,7 @@ library AddressArray { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(address[] memory value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData; diff --git a/packages/world/test/codegen/tables/Bool.sol b/packages/world/test/codegen/tables/Bool.sol index ce8ed61ffa..83d6134ec1 100644 --- a/packages/world/test/codegen/tables/Bool.sol +++ b/packages/world/test/codegen/tables/Bool.sol @@ -191,7 +191,7 @@ library Bool { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bool value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/packages/world/test/codegen/tables/TwoFields.sol b/packages/world/test/codegen/tables/TwoFields.sol index 6bfccb3758..420573e30e 100644 --- a/packages/world/test/codegen/tables/TwoFields.sol +++ b/packages/world/test/codegen/tables/TwoFields.sol @@ -305,7 +305,7 @@ library TwoFields { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(bool value1, bool value2) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value1, value2); diff --git a/packages/world/ts/node/render-solidity/index.ts b/packages/world/ts/node/render-solidity/index.ts index 9411b7acef..67ab048d2d 100644 --- a/packages/world/ts/node/render-solidity/index.ts +++ b/packages/world/ts/node/render-solidity/index.ts @@ -1,4 +1,4 @@ export * from "./renderSystemInterface"; -export * from "./renderWorld"; +export * from "./renderWorldInterface"; export * from "./types"; export * from "./worldgen"; diff --git a/packages/world/ts/node/render-solidity/renderWorld.ts b/packages/world/ts/node/render-solidity/renderWorldInterface.ts similarity index 94% rename from packages/world/ts/node/render-solidity/renderWorld.ts rename to packages/world/ts/node/render-solidity/renderWorldInterface.ts index 42495ef9c6..e5e4eaee16 100644 --- a/packages/world/ts/node/render-solidity/renderWorld.ts +++ b/packages/world/ts/node/render-solidity/renderWorldInterface.ts @@ -7,7 +7,7 @@ import { } from "@latticexyz/common/codegen"; import type { RenderWorldOptions } from "./types"; -export function renderWorld(options: RenderWorldOptions) { +export function renderWorldInterface(options: RenderWorldOptions) { const { interfaceName, storeImportPath, worldImportPath, imports } = options; const baseImports: AbsoluteImportDatum[] = interfaceName === "IBaseWorld" diff --git a/packages/world/ts/node/render-solidity/worldgen.ts b/packages/world/ts/node/render-solidity/worldgen.ts index 7c67686170..a65d63ed8e 100644 --- a/packages/world/ts/node/render-solidity/worldgen.ts +++ b/packages/world/ts/node/render-solidity/worldgen.ts @@ -3,7 +3,7 @@ import path from "path"; import { formatAndWriteSolidity, contractToInterface, type RelativeImportDatum } from "@latticexyz/common/codegen"; import { StoreConfig } from "@latticexyz/store"; import { renderSystemInterface } from "./renderSystemInterface"; -import { renderWorld } from "./renderWorld"; +import { renderWorldInterface } from "./renderWorldInterface"; import { resolveWorldConfig } from "../../config/resolveWorldConfig"; import { WorldConfig } from "../../config/types"; @@ -64,7 +64,7 @@ export async function worldgen( } // render IWorld - const output = renderWorld({ + const output = renderWorldInterface({ interfaceName: config.worldInterfaceName, imports: systemInterfaceImports, storeImportPath: config.storeImportPath, @@ -72,5 +72,5 @@ export async function worldgen( }); // write to file const fullOutputPath = path.join(worldgenBaseDirectory, config.worldInterfaceName + ".sol"); - await formatAndWriteSolidity(output, fullOutputPath, "Generated system interface"); + await formatAndWriteSolidity(output, fullOutputPath, "Generated world interface"); } diff --git a/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol b/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol index 7239ee6e69..28ed85f3bb 100644 --- a/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol +++ b/templates/phaser/packages/contracts/src/codegen/tables/Counter.sol @@ -196,7 +196,7 @@ library Counter { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/templates/react-ecs/packages/contracts/src/codegen/tables/Counter.sol b/templates/react-ecs/packages/contracts/src/codegen/tables/Counter.sol index 7239ee6e69..28ed85f3bb 100644 --- a/templates/react-ecs/packages/contracts/src/codegen/tables/Counter.sol +++ b/templates/react-ecs/packages/contracts/src/codegen/tables/Counter.sol @@ -196,7 +196,7 @@ library Counter { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value); diff --git a/templates/react/packages/contracts/src/codegen/tables/Tasks.sol b/templates/react/packages/contracts/src/codegen/tables/Tasks.sol index ff0552ef06..ffe177c0dc 100644 --- a/templates/react/packages/contracts/src/codegen/tables/Tasks.sol +++ b/templates/react/packages/contracts/src/codegen/tables/Tasks.sol @@ -529,7 +529,7 @@ library Tasks { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode( uint256 createdAt, diff --git a/templates/threejs/packages/contracts/src/codegen/tables/Position.sol b/templates/threejs/packages/contracts/src/codegen/tables/Position.sol index 346fa619ee..156e794bcf 100644 --- a/templates/threejs/packages/contracts/src/codegen/tables/Position.sol +++ b/templates/threejs/packages/contracts/src/codegen/tables/Position.sol @@ -375,7 +375,7 @@ library Position { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(int32 x, int32 y, int32 z) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(x, y, z); diff --git a/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol b/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol index 7239ee6e69..28ed85f3bb 100644 --- a/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol +++ b/templates/vanilla/packages/contracts/src/codegen/tables/Counter.sol @@ -196,7 +196,7 @@ library Counter { * @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 dyanmic (variable length) data, encoded into a sequence of bytes. + * @return The dynamic (variable length) data, encoded into a sequence of bytes. */ function encode(uint32 value) internal pure returns (bytes memory, PackedCounter, bytes memory) { bytes memory _staticData = encodeStatic(value);