Changelog

Version 2.0.11

Release date: Wed May 15 2024

Patch changes

build: bump to node 18.20.2, pnpm 9.1.1 (#2831) (opens in a new tab) (create-mud)

Added pnpm 9 to project's engines.

fix(cli): fixed module artifactPath imports (#2832) (opens in a new tab) (@latticexyz/cli)

Fixed imports of module artifacts via artifactPath and removed unused @latticexyz/world-modules dependency.


Version 2.0.10

Release date: Tue May 14 2024

Patch changes

fix(cli): function selector lookup during deploy (#2800) (opens in a new tab) (@latticexyz/cli)

The deploy CLI now uses logs to find registered function selectors and their corresponding function signatures. Previously only function signatures were fetched via logs and then mapped to function selectors via getRecord calls, but this approach failed for namespaced function selectors of non-root system, because the function signature table includes both the namespaced and non-namespaced signature but the function selector table only includes the namespaced selector that is registered on the world.

feat(cli): deploy with external modules (#2803) (opens in a new tab) (@latticexyz/cli, @latticexyz/world)

Worlds can now be deployed with external modules, defined by a module's artifactPath in your MUD config, resolved with Node's module resolution. This allows for modules to be published to and imported from npm.

 defineWorld({
   // …
   modules: [
     {
-      name: "KeysWithValueModule",
+      artifactPath: "@latticexyz/world-modules/out/KeysWithValueModule.sol/KeysWithValueModule.json",
       root: true,
       args: [resolveTableId("Inventory")],
     },
   ],
 });

Note that the above assumes @latticexyz/world-modules is included as a dependency of your project.

chore: upgrade to ejs 3.1.10 (#2786) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/store, @latticexyz/cli)

Removed the unused ejs dependency.

docs: fix create-mud package name in changeset (#2825) (opens in a new tab) (create-mud)

Templates now use an app namespace by default, instead of the root namespace. This helps keep the root namespace clear for intentionally root-level things and avoids pitfalls with root systems calling other root systems.

chore: upgrade to ejs 3.1.10 (#2786) (opens in a new tab) (@latticexyz/world)

Upgraded the ejs dependency to 3.1.10.

fix(store-indexer): fix distance from follow block metric (#2791) (opens in a new tab) (@latticexyz/store-indexer)

Fixed the distance_from_follow_block gauge to be a positive number if the latest processed block is lagging behind the latest remote block.

fix(common): extend OP contracts, add redstone ones (#2792) (opens in a new tab) (@latticexyz/common)

Added OP predeploy contracts for Redstone and Garnet chain configs and added chain-specific contracts for Redstone chain config.

chore: remove cli faucet command and services package (#2811) (opens in a new tab) (@latticexyz/cli)

Removed broken mud faucet command.

fix(world): config uses readonly arrays (#2805) (opens in a new tab) (@latticexyz/world)

Updated World config types to use readonly arrays.

docs(store-sync): add changeset for #2808 (#2809) (opens in a new tab) (@latticexyz/store-sync)

Both encodeEntity and decodeEntity now use an LRU cache to avoid repeating work during iterations of thousands of entities.

fix(store,world): throw on unexpected config keys (#2797) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

defineStore and defineWorld will now throw a type error if an unexpected config option is used.

chore: remove cli faucet command and services package (#2811) (opens in a new tab) (create-mud)

Removed usages of old testnet faucet in templates. The previous testnet faucet is broken, deprecated, and going offline soon. We'll be replacing the burner account pattern with something better very soon!

chore: bump zod (#2804) (opens in a new tab) (@latticexyz/cli, @latticexyz/config, @latticexyz/faucet, @latticexyz/store-indexer, @latticexyz/store-sync, @latticexyz/store, @latticexyz/world-modules, @latticexyz/world)

Bumped zod dependency to comply with abitype peer dependencies.

feat(store,world): usable enum values from config (#2807) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

defineStore and defineWorld now maps your enums to usable, strongly-typed enums on enumValues.

const config = defineStore({
  enums: {
    TerrainType: ["Water", "Grass", "Sand"],
  },
});
 
config.enumValues.TerrainType.Water;
//                              ^? (property) Water: 0
 
config.enumValues.TerrainType.Grass;
//                              ^? (property) Grass: 1

This allows for easier referencing of enum values (i.e. uint8 equivalent) in contract calls.

writeContract({
  // …
  functionName: "setTerrainType",
  args: [config.enumValues.TerrainType.Grass],
});

Version 2.0.9

Release date: Wed May 01 2024

Patch changes

fix(cli): do not require PRIVATE_KEY if using KMS (#2765) (opens in a new tab) (@latticexyz/cli)

Fixed mud deploy to not require the PRIVATE_KEY environment variable when using a KMS signer.

feat(create-mud): redstone and garnet chains (#2776) (opens in a new tab) (create-mud)

Updated templates with Redstone and Garnet chains and removed the deprecated Lattice testnet chain.

feat(store-indexer): add metric for distance from block tag to follow (#2763) (opens in a new tab) (@latticexyz/store-indexer)

Added a distance_from_follow_block metric to compare the latest stored block number with the block number corresponding to the block tag the indexer follows.

feat(cli): blockscout is default verifier (#2775) (opens in a new tab) (@latticexyz/cli)

mud verify now defaults to blockscout if no --verifier is provided.

fix(cli): run postdeploy with aws flag when kms is enabled (#2766) (opens in a new tab) (@latticexyz/cli)

Fixed mud deploy to use the forge script --aws flag when executing PostDeploy with a KMS signer.

Note that you may need to update your PostDeploy.s.sol script, with vm.startBroadcast receiving no arguments instead of reading a private key from the environment:

-uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
-vm.startBroadcast(deployerPrivateKey);
 
+vm.startBroadcast();

feat(common): add indexer URL to chain configs (#2771) (opens in a new tab) (@latticexyz/store-sync)

Updated createStoreSync to default to the chain's indexer URL when no indexerUrl is passed in. To intentionally unset the value and not use the indexer at all, indexerUrl can now also be false.

fix(cli): remove postdeploy gas setting in favor of script options (#2756) (opens in a new tab) (@latticexyz/cli)

Removed manual gas setting in PostDeploy step of mud deploy in favor of forge script fetching it from the RPC.

If you still want to manually set gas, you can use mud deploy --forgeScriptOptions="--with-gas-price 1000000".

refactor(common,cli): kms deployer gets keyId from environment (#2760) (opens in a new tab) (@latticexyz/cli)

The key ID for deploying via KMS signer is now set via an AWS_KMS_KEY_ID environment variable to better align with Foundry tooling. To enable KMS signing with this environment variable, use the --kms flag.

-mud deploy --awsKmsKeyId [key ID]
+AWS_KMS_KEY_ID=[key ID] mud deploy --kms

feat(common): add indexer URL to chain configs (#2771) (opens in a new tab) (@latticexyz/common)

Added an optional indexerUrl property to MUDChain, and populated it in the Redstone and Garnet chain configs.

feat(common): add chain icons (#2778) (opens in a new tab) (@latticexyz/common)

Added chain icons to Redstone and Garnet chain configs via chain.iconUrls.


Version 2.0.8

Release date: Sat Apr 27 2024

Patch changes

fix(store-indexer): allow empty env variable (#2746) (opens in a new tab) (@latticexyz/store-indexer)

Added support for an empty STORE_ADDRESS= environment variable. This previously would fail the input validation, now it behaves the same way as not setting the STORE_ADDRESS variable at all.

fix(cli): fix verify with sourcify for dependencies (#2750) (opens in a new tab) (@latticexyz/cli)

Patched mud verify to properly verify store, world, and world-modules contracts. Currently only sourcify is fully supported and is the default verifier.

feat(common): add redstone chain config (#2749) (opens in a new tab) (@latticexyz/common)

Added Garnet testnet and Redstone mainnet chain configs and deprecated Lattice Testnet.

import { garnet, redstone } from "@latticexyz/common/chains";

Version 2.0.7

Release date: Thu Apr 25 2024

Patch changes

feat(store-indexer): add prometheus metrics (#2739) (opens in a new tab) (@latticexyz/store-indexer)

Add Prometheus metrics at /metrics to the Postgres indexer backend and frontend, as well as the SQLite indexer.

fix(common): use feeRef for sendTransaction calls (#2725) (opens in a new tab) (@latticexyz/common)

Added asynchronous polling for current fees to sendTransaction.

fix(block-logs-stream): handle proxyd errors (#2726) (opens in a new tab) (@latticexyz/block-logs-stream)

Added detection and handling for proxyd rate limit and block range errors.

feat(cli): deploy with kms (#2704) (opens in a new tab) (@latticexyz/cli)

Added a --awsKmsKeyId flag to mud deploy that deploys the world using an AWS KMS key as a transaction signer.

fix(store-sync): await fetchAndStoreLogs (#2702) (opens in a new tab) (@latticexyz/store-sync)

Partially revert #2665 (opens in a new tab) to guarantee logs are stored in order.

fix(cli): add retry to getLogs when getting resource ID's (#2709) (opens in a new tab) (@latticexyz/cli)

Deploying now retries on "block is out of range" errors, for cases where the RPC is load balanced and out of sync.

feat(cli): manually fetch gas price from rpc before PostDeploy runs (#2638) (opens in a new tab) (@latticexyz/cli)

Deploy will now fetch and set the gas price during execution of PostDeploy script. This should greatly reduce the fees paid for L2s.

fix(world-modules): properly concat baseURI and tokenURI in ERC721 module (#2686) (opens in a new tab) (@latticexyz/world-modules)

Fixed ERC721 module to properly encode token ID as part of token URI.

feat(cli): verify command (#2662) (opens in a new tab) (@latticexyz/cli)

Added a new mud verify command which verifies all contracts in a project. This includes systems, modules, the WorldFactory and World.

fix(common): kms correctly serializes transactions (#2721) (opens in a new tab) (@latticexyz/common)

Added kmsKeyToAccount, a viem custom account (opens in a new tab) that signs transactions using AWS KMS.

To use it, you must first install @aws-sdk/client-kms@3.x and asn1.js@5.x dependencies into your project. Then create a KMS account with:

import { kmsKeyToAccount } from "@latticexyz/common/kms";
const account = kmsKeyToAccount({ keyId: ... });

By default, a KMSClient will be created, but you can also pass one in via the client option. The default KMS client will use your environment's AWS SDK configuration (opens in a new tab).

fix(cli): fix deployer warning (#2683) (opens in a new tab) (@latticexyz/cli)

Fixed an issue where deploys were warning about mismatched bytecode when the bytecode was correct and what we expect.

fix(create-mud): make worlds.json address type more specific (#2685) (opens in a new tab) (create-mud)

Made worlds.json's address type more like viem's Hex type so it's easy to pass through as an argument.

refactor(world,cli): rename useProxy to upgradeableWorldImplementation (#2732) (opens in a new tab) (@latticexyz/world, @latticexyz/cli)

Added a deploy.upgradeableWorldImplementation option to the MUD config that deploys the World as an upgradeable proxy contract. The proxy behaves like a regular World contract, but the underlying implementation can be upgraded by calling setImplementation.

fix(store): enforce unique table names across types (#2736) (opens in a new tab) (@latticexyz/store)

Added a check to registerTable that prevents registering both an offchain and onchain table with the same name, making it easier to use human-readable names in indexers.

feat(world-modules): string systemId in callWithSignature typehash (#2700) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/world)

Replaced the systemId field in the Unstable_CallWithSignatureSystem typehash with individual systemNamespace and systemName string fields.

feat(cli): add user-specified PostDeploy forge options (#2703) (opens in a new tab) (@latticexyz/cli)

Added a --forgeScriptOptions flag to deploy and dev commands to allow passing in additional CLI flags to forge script command.

fix(common): make Resource type props readonly (#2516) (opens in a new tab) (@latticexyz/common)

Resource type props are now readonly.


Version 2.0.6

Release date: Wed Apr 17 2024

Patch changes

feat(store-indexer): add cache headers (#2669) (opens in a new tab) (@latticexyz/store-indexer)

Added Cache-Control and Content-Type headers to the postgres indexer API.

fix(common): latency improvements (#2641) (opens in a new tab) (@latticexyz/common)

Reduced the number of RPC requests before sending a transaction in the transactionQueue viem decorator.

fix(store,world): fix StoreRead.getDynamicFieldLength (#2680) (opens in a new tab) (@latticexyz/store)

Patched StoreRead.getDynamicFieldLength to properly read StoreCore.getDynamicFieldLength.

Previously StoreRead.getDynamicFieldLength incorrectly read from StoreCore.getFieldLength, which expected a fieldIndex instead of a dynamicFieldIndex, and thereby returned an invalid result if the table had both static and dynamic fields (in which case fieldIndex != dynamicFieldIndex). StoreRead is used for external reads from the Store/World contract, so this bug only materialized in external table reads (ie from Systems outside the root namespace) of the dynamic length of a field in a table with both static and dynamic fields.

feat(world-modules): callWithSignature chain id is salt (#2648) (opens in a new tab) (@latticexyz/world-modules)

Moved the chain ID in CallWithSignature from the domain.chainId to the domain.salt field to allow for cross-chain signing without requiring wallets to switch networks. The value of this field should be the chain on which the world lives, rather than the chain the wallet is connected to.

refactor(store,world): refactor types, remove redundant casts (#2555) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Internal type improvements.

chore: bump viem to 2.9.20 (#2681) (opens in a new tab) (@latticexyz/block-logs-stream, @latticexyz/cli, @latticexyz/common, @latticexyz/config, @latticexyz/dev-tools, @latticexyz/faucet, @latticexyz/protocol-parser, @latticexyz/query, @latticexyz/schema-type, @latticexyz/store-indexer, @latticexyz/store-sync, @latticexyz/store, @latticexyz/world, create-mud)

Bumped viem to 2.9.20.

docs: add changeset for #2645 (#2647) (opens in a new tab) (create-mud, @latticexyz/block-logs-stream, @latticexyz/protocol-parser, @latticexyz/store-indexer, @latticexyz/schema-type, @latticexyz/store-sync, @latticexyz/dev-tools, @latticexyz/common, @latticexyz/config, @latticexyz/faucet, @latticexyz/query, @latticexyz/store, @latticexyz/world, @latticexyz/cli)

Bumped viem to 2.9.16.

docs: add changeset for devtools top up (#2658) (opens in a new tab) (@latticexyz/dev-tools)

Added a "top up" button to account balance when running on anvil.

fix(store-sync): reduce latency in waitForTransaction (#2665) (opens in a new tab) (@latticexyz/store-sync)

Small optimizations in waitForTransaction to parallelize network requests.

feat(store-sync): add status and block number to return type of waitForTransaction (#2668) (opens in a new tab) (@latticexyz/store-sync)

waitForTransaction now returns a Promise<{ blockNumber: bigint, status: "success" | "reverted" }> instead of Promise<void>, to allow consumers to react to reverted transactions without refetching the transaction receipt.


Version 2.0.5

Release date: Fri Apr 12 2024

Patch changes

fix(world-modules): add missing interfaces (#2605) (opens in a new tab) (@latticexyz/world-modules)

Added missing system interfaces for ERC721, UniqueEntity, and CallWithSignature modules.

fix(common): pass through rest of nonce manager opts (#2616) (opens in a new tab) (@latticexyz/common)

Fixed getNonceManager to correctly pass all options to createNonceManager.

feat(world-modules): add validateCallWithSignature to Unstable_CallWithSignatureModule (#2614) (opens in a new tab) (@latticexyz/world-modules)

Added validateCallWithSignature function to Unstable_CallWithSignatureModule to validate a signature without executing the call.

fix(world-modules): explicitly export mud config (#2598) (opens in a new tab) (@latticexyz/world-modules)

Exported mud config as internal.

feat(create-mud): change anvil to create a block every two seconds (#2635) (opens in a new tab) (create-mud)

Updated anvil args with two second block time to better reflect L2s

fix(store): return zero for uninitialised static array elements (#2613) (opens in a new tab) (@latticexyz/store)

Fixed the behaviour of static arrays, so that they return zero for uninitialised values, to mirror the native Solidity behavior. Previously they reverted with Store_IndexOutOfBounds if the index had not been set yet.

chore: changeset for callWithSignature (#2601) (opens in a new tab) (@latticexyz/cli, @latticexyz/world-modules, @latticexyz/world)

Replaced the Unstable_DelegationWithSignatureModule preview module with a more generalized Unstable_CallWithSignatureModule that allows making arbitrary calls (similar to callFrom).

This module is still marked as Unstable, because it will be removed and included in the default World deployment once it is audited.


Version 2.0.4

Release date: Tue Apr 02 2024

Patch changes

feat(common): allow specifying concurrency in transactionQueue (#2589) (opens in a new tab) (@latticexyz/common)

transactionQueue now accepts a queueConcurrency to allow adjusting the number of concurrent calls to the mempool. This defaults to 1 to ensure transactions are ordered and nonces are handled properly. Any number greater than that is likely to see nonce errors and transactions arriving out of order, but this may be an acceptable trade-off for some applications that can safely retry.


Version 2.0.3

Release date: Tue Apr 02 2024

Patch changes

feat(common,world): improvements for smart accounts (#2578) (opens in a new tab) (@latticexyz/common)

transactionQueue decorator now accepts an optional publicClient argument, which will be used in place of the extended viem client for making public action calls (getChainId, getTransactionCount, simulateContract, call). This helps in cases where the extended viem client is a smart account client, like in permissionless.js (opens in a new tab), where the transport is the bundler, not an RPC.

writeObserver decorator now accepts any Client, not just a WalletClient.

createBurnerAccount now returns a PrivateKeyAccount, the more specific Account type.

feat(common,world): improvements for smart accounts (#2578) (opens in a new tab) (@latticexyz/world)

callFrom decorator now accepts any Client, not just a WalletClient. It also no longer attempts to wrap/redirect calls to call, callFrom, and registerDelegationWithSignature.


Version 2.0.2

Release date: Mon Apr 01 2024

Patch changes

feat(world-modules): register delegation with signature (#2480) (opens in a new tab) (@latticexyz/cli, @latticexyz/world-modules, @latticexyz/world)

Added a new preview module, Unstable_DelegationWithSignatureModule, which allows registering delegations with a signature.

Note: this module is marked as Unstable, because it will be removed and included in the default World deployment once it is audited.

chore: threejs template changeset (#2529) (opens in a new tab) (create-mud)

Changed the controls in the threejs template from arrow keys to WASD and added text to explain what the app does.

docs: clarify callFrom changelog (#2579) (opens in a new tab) (@latticexyz/world)

Added a viem client decorator for account delegation. By extending viem clients with this function after delegation, the delegation is automatically applied to World contract writes. This means that these writes are made on behalf of the delegator. Internally, it transforms the write arguments to use callFrom.

This is an internal feature and is not ready for stable consumption yet, so it's not yet exported. Its API may change.

When using with a viem public client, system function selectors will be fetched from the world:

walletClient.extend(
  callFrom({
    worldAddress,
    delegatorAddress,
    publicClient,
  }),
);

Alternatively, a worldFunctionToSystemFunction handler can be passed in that will translate between world function selectors and system function selectors for cases where you want to provide your own behavior or use data already cached in e.g. Zustand or RECS.

walletClient.extend(
  callFrom({
    worldAddress,
    delegatorAddress,
    worldFunctionToSystemFunction: async (worldFunctionSelector) => {
      const systemFunction = useStore.getState().getValue(tables.FunctionSelectors, { worldFunctionSelector })!;
      return {
        systemId: systemFunction.systemId,
        systemFunctionSelector: systemFunction.systemFunctionSelector,
      };
    },
  }),
);

refactor(cli): remove forge cache workaround (#2576) (opens in a new tab) (@latticexyz/cli)

Remove workaround for generating IWorld interface from cached forge files as this was fixed by forge.

fix(create-mud): run anvil in its own process (#2538) (opens in a new tab) (create-mud)

Templates now run anvil in its own process (via mprocs) for better visibility into anvil logs.


Version 2.0.1

Release date: Thu Mar 21 2024

Patch changes

fix(store,world): minor config validation fixes (#2517) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Minor fixes to config input validations:

  • systems.openAccess incorrectly expected true as the only valid input. It now allows boolean.
  • The config complained if parts of it were defined as const outside the config input. This is now possible.
  • Shorthand inputs are now enabled.

Version 2.0.0

Release date: Thu Mar 21 2024


Version 2.0.0-next.18

Release date: Thu Mar 21 2024

Major changes

docs: add store/world config changesets (#2497) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Store and World configs have been rebuilt with strong types. The shape of these configs have also changed slightly for clarity, the biggest change of which is merging of keySchema and valueSchema into a single schema with a separate key for a table's primary key.

To migrate, first update the imported config method:

mud.config.ts
-import { mudConfig } from "@latticexyz/world/register";
+import { defineWorld } from "@latticexyz/world";
 
-export default mudConfig({
+export default defineWorld({

Note that if you are only using Store, you will need to import defineStore from @latticexyz/store.

Then migrate the table key by renaming keySchema to schema and define the table key with each field name from your key schema:

mud.config.ts
 export default defineWorld({
   tables: {
     Position: {
-      keySchema: {
+      schema: {
         player: "address",
       },
       valueSchema: {
         x: "int32",
         y: "int32",
       },
+      key: ['player'],
     },
   },
 });

Now we can merge the valueSchema into schema.

mud.config.ts
 export default defineWorld({
   tables: {
     Position: {
       schema: {
         player: "address",
-      },
-      valueSchema: {
         x: "int32",
         y: "int32",
       },
       key: ['player'],
     },
   },
 });

If you previously used the table config shorthand without the full keySchema and valueSchema, some of the defaults have changed. Shorthands now use an id: "bytes32" field by default rather than key: "bytes32" and corresponding key: ["id"]. To keep previous behavior, you may have to manually define your schema with the previous key and value fields.

mud.config.ts
 export default defineWorld({
   tables: {
-    OwnedBy: "address",
+    OwnedBy: {
+      schema: {
+        key: "bytes32",
+        value: "address",
+      },
+      key: ["key"],
+    },
   },
 });

Singleton tables are defined similarly, where an empty key rather than keySchema is provided:

mud.config.ts
-keySchema: {}
+key: []

Offchain tables are now defined as a table type instead an offchainOnly boolean:

mud.config.ts
-offchainOnly: true
+type: 'offchainTable'

All codegen options have moved under codegen:

mud.config.ts
 export default defineWorld({
-  codegenDirectory: "…",
+  codegen: {
+    outputDirectory: "…",
+  },
   tables: {
     Position: {
       schema: {
         player: "address",
         x: "int32",
         y: "int32",
       },
       key: ['player'],
-      directory: "…",
-      dataStruct: false,
+      codegen: {
+        outputDirectory: "…",
+        dataStruct: false,
+      },
     },
   },
 });

refactor: move table ID and field layout constants into table library (#2327) (opens in a new tab) (create-mud, @latticexyz/cli, @latticexyz/common, @latticexyz/store, @latticexyz/world, @latticexyz/world-modules)

Moved table ID and field layout constants in code-generated table libraries from the file level into the library, for clearer access and cleaner imports.

-import { SomeTable, SomeTableTableId } from "./codegen/tables/SomeTable.sol";
+import { SomeTable } from "./codegen/tables/SomeTable.sol";
 
-console.log(SomeTableTableId);
+console.log(SomeTable._tableId);
 
-console.log(SomeTable.getFieldLayout());
+console.log(SomeTable._fieldLayout);

feat(store,world): set protocol version, add tests (#2412) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Set the protocol version to 2.0.0 for each Store and World.

refactor(schema-type,protocol-parser): explicit internal vs external exports (#2452) (opens in a new tab) (@latticexyz/protocol-parser, @latticexyz/schema-type)

Moved all existing exports to a /internal import path to indicate that these are now internal-only and deprecated. We'll be replacing these types and functions with new ones that are compatible with our new, strongly-typed config.

feat(store): add field index to Store_SpliceDynamicData event (#2279) (opens in a new tab) (@latticexyz/store, @latticexyz/store-sync)

Added dynamicFieldIndex to the Store_SpliceDynamicData event. This enables indexers to store dynamic data as a blob per dynamic field without a schema lookup.

refactor: rename PackedCounter to EncodedLengths (#2490) (opens in a new tab) (@latticexyz/cli, @latticexyz/protocol-parser, @latticexyz/store, @latticexyz/world-modules, @latticexyz/world, create-mud)

Renamed PackedCounter to EncodedLengths for consistency.

feat(store-sync): adjust DB schema/table names for consistency (#2379) (opens in a new tab) (@latticexyz/store-indexer, @latticexyz/store-sync)

PostgreSQL sync/indexer now uses {storeAddress} for its database schema names and {namespace}__{tableName} for its database table names (or just {tableName} for root namespace), to be more consistent with the rest of the MUD codebase.

For namespaced tables:

- SELECT * FROM 0xfff__some_ns.some_table
+ SELECT * FROM 0xfff.some_ns__some_table

For root tables:

- SELECT * FROM 0xfff__.some_table
+ SELECT * FROM 0xfff.some_table

SQLite sync/indexer now uses snake case for its table names and column names for easier writing of queries and to better match PostgreSQL sync/indexer naming.

- SELECT * FROM 0xfFf__someNS__someTable
+ SELECT * FROM 0xfff__some_ns__some_table

feat: use new config (#2483) (opens in a new tab) (@latticexyz/cli, @latticexyz/dev-tools, @latticexyz/store-sync, @latticexyz/store, @latticexyz/world-modules, @latticexyz/world, create-mud)

Migrated to new config format.

Minor changes

feat(cli): add a RPC batch option to cli (#2322) (opens in a new tab) (@latticexyz/cli)

Added an --rpcBatch option to mud deploy command to batch RPC calls for rate limited RPCs.

feat(store-sync,store-indexer): add followBlockTag option (#2315) (opens in a new tab) (@latticexyz/store-indexer, @latticexyz/store-sync)

Added a followBlockTag option to configure which block number to follow when running createStoreSync. It defaults to latest (current behavior), which is recommended for individual clients so that you always have the latest chain state.

Indexers now default to safe to avoid issues with reorgs and load-balanced RPCs being out of sync. This means indexers will be slightly behind the latest block number, but clients can quickly catch up. Indexers can override this setting using FOLLOW_BLOCK_TAG environment variable.

refactor(world): registerRootFunctionSelector takes system signature (#2395) (opens in a new tab) (@latticexyz/world)

registerRootFunctionSelector now expects a systemFunctionSignature instead of a systemFunctionSelector. Internally, we compute the selector from the signature. This allows us to track system function signatures that are registered at the root so we can later generate ABIs for these systems.

feat(common): add viem actions that work the same as the current wrappers (#2347) (opens in a new tab) (@latticexyz/common, create-mud)

Added viem custom client actions that work the same as MUD's now-deprecated getContract, writeContract, and sendTransaction wrappers. Templates have been updated to reflect the new patterns.

You can migrate your own code like this:

-import { createWalletClient } from "viem";
-import { getContract, writeContract, sendTransaction } from "@latticexyz/common";
+import { createWalletClient, getContract } from "viem";
+import { transactionQueue, writeObserver } from "@latticexyz/common/actions";
 
-const walletClient = createWalletClient(...);
+const walletClient = createWalletClient(...)
+  .extend(transactionQueue())
+  .extend(writeObserver({ onWrite });
 
 const worldContract = getContract({
   client: { publicClient, walletClient },
-  onWrite,
 });

refactor(store): add StoreWrite and Store abstract contracts (#2411) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Added an abstract StoreKernel contract, which includes all Store interfaces except for registration, and implements write methods, protocolVersion and initializes StoreCore. Store extends StoreKernel with the IStoreRegistration interface. StoreData is removed as a separate interface/contract. World now extends StoreKernel (since the registration methods are added via the InitModule).

refactor(store): make static array length a constant (#2410) (opens in a new tab) (@latticexyz/store)

Replaced the static array length getters in table libraries with constants.

feat(gas-report): run gas report with --isolate (#2331) (opens in a new tab) (@latticexyz/gas-report)

Now uses --isolate flag in forge test for more accurate gas measurement.

feat(cli): link and deploy public libraries (#1910) (opens in a new tab) (@latticexyz/cli)

mud deploy now supports public/linked libraries.

This helps with cases where system contracts would exceed the EVM bytecode size limit and logic would need to be split into many smaller systems.

Instead of the overhead and complexity of system-to-system calls, this logic can now be moved into public libraries that will be deployed alongside your systems and automatically delegatecalled.

refactor: hardcode key/value schema in table libraries (#2328) (opens in a new tab) (create-mud, @latticexyz/store, @latticexyz/world, @latticexyz/world-modules)

Moved key schema and value schema methods to constants in code-generated table libraries for less bytecode and less gas in register/install methods.

-console.log(SomeTable.getKeySchema());
+console.log(SomeTable._keySchema);
 
-console.log(SomeTable.getValueSchema());
+console.log(SomeTable._valueSchema);

feat: upgrade viem to v2 (#2284) (opens in a new tab) (@latticexyz/block-logs-stream, @latticexyz/cli, @latticexyz/common, @latticexyz/config, @latticexyz/dev-tools, @latticexyz/faucet, @latticexyz/protocol-parser, @latticexyz/schema-type, @latticexyz/store-indexer, @latticexyz/store-sync, @latticexyz/store, @latticexyz/world, create-mud)

Upgraded all packages and templates to viem v2.7.12 and abitype v1.0.0.

Some viem APIs have changed and we've updated getContract to reflect those changes and keep it aligned with viem. It's one small code change:

 const worldContract = getContract({
   address: worldAddress,
   abi: IWorldAbi,
-  publicClient,
-  walletClient,
+  client: { public: publicClient, wallet: walletClient },
 });

Patch changes

fix(cli): throw error when deploying overlapping systems (#2325) (opens in a new tab) (@latticexyz/cli, @latticexyz/world)

Attempting to deploy multiple systems where there are overlapping system IDs now throws an error.

fix(common): use setTimeout as fallback for requestIdleCallback (#2406) (opens in a new tab) (@latticexyz/common)

waitForIdle now falls back to setTimeout for environments without requestIdleCallback.

refactor: human-readable resource IDs use double underscore (#2310) (opens in a new tab) (@latticexyz/store-sync, @latticexyz/dev-tools, @latticexyz/common, @latticexyz/cli)

Updated all human-readable resource IDs to use {namespace}__{name} for consistency with world function signatures.

chore: remove some unused files (#2398) (opens in a new tab) (@latticexyz/common, @latticexyz/react)

Removed some unused files, namely curry in @latticexyz/common and useDeprecatedComputedValue from @latticexyz/react.

fix(world-modules): token modules always register namespace (#2352) (opens in a new tab) (@latticexyz/world-modules)

ERC20 and ERC721 implementations now always register the token namespace, instead of checking if it has already been registered. This prevents issues with registering the namespace beforehand, namely that only the owner of a system can create a puppet for it.

refactor(store): store core imports store events (#2356) (opens in a new tab) (@latticexyz/store)

Refactored StoreCore to import IStoreEvents instead of defining the events twice.

feat(world): emit salt in WorldDeployed event (#2301) (opens in a new tab) (@latticexyz/world)

Added salt to the WorldDeployed event.

chore: upgrade to typescript 5.4.2 (#2397) (opens in a new tab) (@latticexyz/cli, create-mud)

Bumped typescript to 5.4.2, eslint to 8.57.0, and both @typescript-eslint/eslint-plugin and @typescript-eslint/parser to 7.1.1.

fix(common): remove underscore prefix from root namespace labels (#2400) (opens in a new tab) (@latticexyz/common)

resourceToLabel now correctly returns just the resource name if its in the root namespace.

chore(noise): remove noise package (#2304) (opens in a new tab) (@latticexyz/noise)

Removed the @latticexyz/noise package.

refactor(store): event interfaces for Store libraries (#2348) (opens in a new tab) (@latticexyz/store)

Added interfaces for all errors that are used by StoreCore, which includes FieldLayout, PackedCounter, Schema, and Slice. This interfaces are inherited by IStore, ensuring that all possible errors are included in the IStore ABI for proper decoding in the frontend.

docs: add missing changeset (#2374) (opens in a new tab) (@latticexyz/common)

Moved the transaction simulation step to just before sending the transaction in our transaction queue actions (sendTransaction and writeContract).

This helps avoid cascading transaction failures for deep queues or when a transaction succeeding depends on the value of the previous.

fix(store): restore bytesN helpers (#2403) (opens in a new tab) (@latticexyz/store)

Restored Bytes.sliceN helpers that were previously (mistakenly) removed and renamed them to Bytes.getBytesN.

If you're upgrading an existing MUD project, you can rerun codegen with mud build to update your table libraries to the new function names.

chore: upgrade prettier to 3.2.5 and prettier-plugin-solidity to 1.3.1 (#2303) (opens in a new tab) (@latticexyz/common)

Upgraded prettier version to 3.2.5 and prettier-plugin-solidity version to 1.3.1.

feat(world): add system signatures to FunctionSignatures (#2392) (opens in a new tab) (@latticexyz/world)

Added system signatures to the FunctionSignatures table, so they can be used to generate system ABIs and decode system calls made via the world.

fix(gas-report): update filename matcher (#2277) (opens in a new tab) (@latticexyz/gas-report)

Fixed gas report parsing for foundry versions released after 2024-02-15.

feat(create-mud): add additional recommended vscode extensions (#2440) (opens in a new tab) (create-mud)

Added dbaeumer.vscode-eslint and esbenp.prettier-vscode to recommended VSCode extensions.

refactor(world): add IWorldEvents with HelloWorld (#2358) (opens in a new tab) (@latticexyz/world)

Created an IWorldEvents interface with HelloStore, so all World events are defined in a single interface.

fix(store-sync): track changed records together in zustand (#2387) (opens in a new tab) (@latticexyz/store-sync)

Fixes an issue with Zustand store sync where multiple updates to a record for a key in the same block did not get tracked and applied properly.

refactor(store): hellostore in IStoreEvents (#2357) (opens in a new tab) (@latticexyz/store)

Moved the HelloStore to IStoreEvents so all Store events are defined in the same interface.

feat(world): world kernel inherits IModuleErrors (#2380) (opens in a new tab) (@latticexyz/world)

IWorldKernel now inherits IModuleErrors so it can render the correct errors if the World reverts when delegatecalled with Module code.

feat(cli): deterministic deployer fallback (#2261) (opens in a new tab) (@latticexyz/cli)

Added a non-deterministic fallback for deploying to chains that have replay protection on and do not support pre-EIP-155 transactions (no chain ID).

If you're using mud deploy and there's already a deterministic deployer (opens in a new tab) on your target chain, you can provide the address with --deployerAddress 0x... to still get some determinism.

refactor(world): rename functionSelector to worldFunctionSelector (#2391) (opens in a new tab) (@latticexyz/world)

Renamed the functionSelector key in the FunctionSelectors table to worldFunctionSelector. This clarifies that FunctionSelectors is for world function selectors and can be used to generate the world ABI.


Version 2.0.0-next.17

Release date: Tue Feb 20 2024

Major changes

chore: upgrade to Solidity 0.8.24 (#2202) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/schema-type, @latticexyz/gas-report, @latticexyz/common, @latticexyz/noise, @latticexyz/store, @latticexyz/world, @latticexyz/cli, create-mud)

Bumped Solidity version to 0.8.24.

feat(world): rename CoreModule to InitModule (#2227) (opens in a new tab) (@latticexyz/world)

Renamed CoreModule to InitModule and CoreRegistrationSystem to RegistrationSystem.

feat(cli,world): add user defined salt in WorldFactory.deployWorld() (#2219) (opens in a new tab) (@latticexyz/cli, @latticexyz/world)

WorldFactory now expects a user-provided salt when calling deployWorld(...) (instead of the previous globally incrementing counter). This enables deterministic world addresses across different chains.

When using mud deploy, you can provide a bytes32 hex-encoded salt using the --salt option, otherwise it defaults to a random hex value.

feat(store): rename StoreCore.registerCoreTables to registerInternalTables (#2225) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Renamed StoreCore's registerCoreTables method to registerInternalTables.

Minor changes

fix(world-modules): SystemSwitch properly calls systems from root (#2205) (opens in a new tab) (@latticexyz/world-modules)

Fixed SystemSwitch to properly call non-root systems from root systems.

feat(store-sync): wait for idle after each chunk of logs in a block (#2254) (opens in a new tab) (@latticexyz/store-sync)

createStoreSync now waits for idle (opens in a new tab) between each chunk of logs in a block to allow for downstream render cycles to trigger. This means that hydrating logs from an indexer will no longer block until hydration completes, but rather allow for onProgress callbacks to trigger.

feat(world): deployment salt by msg.sender (#2210) (opens in a new tab) (@latticexyz/world)

WorldFactory now derives a salt based on number of worlds deployed by msg.sender, which should help with predictable world deployments across chains.

Patch changes

feat(cli): hardcode table ID with codegen (#2229) (opens in a new tab) (@latticexyz/cli, @latticexyz/common, @latticexyz/store, @latticexyz/world-modules, @latticexyz/world, create-mud)

Table libraries now hardcode the bytes32 table ID value rather than computing it in Solidity. This saves a bit of gas across all storage operations.

fix(store): reorder core table registration (#2164) (opens in a new tab) (@latticexyz/store)

Fixed a race condition when registering core tables, where we would set a record in the ResourceIds table before the table was registered.

fix(world): check table exists for register store and system hook [L-09] (#2195) (opens in a new tab) (@latticexyz/world)

Updated WorldRegistrationSystem to check that systems exist before registering system hooks.

fix(store-sync): fix overflowing column types, bump postgres sync version (#2270) (opens in a new tab) (@latticexyz/store-sync)

Bumped the Postgres column size for int32, uint32, int64, and uint64 types to avoid overflows

feat(store-sync): bool array column types for decoded indexer (#2283) (opens in a new tab) (@latticexyz/store-sync)

Moved boolean array types to use array column types (instead of JSON columns) for the Postgres decoded indexer

docs: add missing changeset (#2282) (opens in a new tab) (@latticexyz/store-sync)

Moved numerical array types to use array column types (instead of JSON columns) for the Postgres decoded indexer

docs: changeset for #2187 (#2188) (opens in a new tab) (@latticexyz/cli)

Fixed registration of world signatures/selectors for namespaced systems. We changed these signatures in #2160 (opens in a new tab), but missed updating part of the deploy step.

fix(common): include only errors defined in the contract (#2194) (opens in a new tab) (@latticexyz/common)

Prevented errors not included in the contract (but present in the file) from being included in the interface by contractToInterface

refactor(store): push to StoreHooks with StoreCore method (#2201) (opens in a new tab) (@latticexyz/store)

Refactored StoreCore.registerStoreHook to use StoreHooks._push for gas efficiency.

refactor(world,world-modules): rename module args to encodedArgs (#2199) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/world)

Renamed the Module args parameter to encodedArgs to better reflect that it is ABI-encoded arguments.

feat(world): rename CoreModule to InitModule (#2227) (opens in a new tab) (@latticexyz/cli)

Updated deployer with world's new InitModule naming.

fix(world): prevent namespace from ending with underscore [M-05] (#2182) (opens in a new tab) (@latticexyz/world)

Added a check to prevent namespaces from ending with an underscore (which could cause problems with world function signatures).

fix(world): check table exists for register store and system hook [L-09] (#2195) (opens in a new tab) (@latticexyz/store)

Updated StoreCore to check that tables exist before registering store hooks.


Version 2.0.0-next.16

Release date: Tue Jan 23 2024

Major changes

feat(world): remove system name from function signatures/selectors [M-05] (#2160) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/world)

World function signatures for namespaced systems have changed from {namespace}_{systemName}_{functionName} to {namespace}__{functionName} (double underscore, no system name). This is more ergonomic and is more consistent with namespaced resources in other parts of the codebase (e.g. MUD config types, table names in the schemaful indexer).

If you have a project using the namespace key in your mud.config.ts or are manually registering systems and function selectors on a namespace, you will likely need to codegen your system interfaces (pnpm build) and update any calls to these systems through the world's namespaced function signatures.

chore: add module addresses changeset (#2172) (opens in a new tab) (@latticexyz/world, @latticexyz/world-modules)

Refactored InstalledModules to key modules by addresses instead of pre-defined names. Previously, modules could report arbitrary names, meaning misconfigured modules could be installed under a name intended for another module.

feat(world): require namespace to exist before registering systems/tables in it [C-01] (#2007) (opens in a new tab) (@latticexyz/cli, @latticexyz/world-modules, @latticexyz/world)

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.

+  world.registerNamespace(namespaceId);
   world.registerSystem(systemId, system, true);
+  world.registerNamespace(namespaceId);
   MyTable.register();

refactor(cli,world,world-modules): split and separately deploy core systems (#2128) (opens in a new tab) (@latticexyz/cli)

Separated core systems deployment from CoreModule, and added the systems as arguments to CoreModule

refactor(cli,world,world-modules): split and separately deploy core systems (#2128) (opens in a new tab) (@latticexyz/world)

  • Split CoreSystem into AccessManagementSystem, BalanceTransferSystem, BatchCallSystem, CoreRegistrationSystem
  • Changed CoreModule to receive the addresses of these systems as arguments, instead of deploying them
  • Replaced CORE_SYSTEM_ID constant with ACCESS_MANAGEMENT_SYSTEM_ID, BALANCE_TRANSFER_SYSTEM_ID, BATCH_CALL_SYSTEM_ID, CORE_REGISTRATION_SYSTEM_ID, for each respective system

These changes separate the initcode of CoreModule from the bytecode of core systems, which effectively removes a limit on the total bytecode of all core systems.

feat(world): prevent invalid namespace strings [M-05] (#2169) (opens in a new tab) (@latticexyz/world)

Namespaces are not allowed to contain double underscores ("__") anymore, as this sequence of characters is used to separate the namespace and function selector (opens in a new tab) in namespaced systems. This is to prevent signature clashes of functions in different namespaces.

(Example: If namespaces were allowed to contain this separator string, a function "function" in namespace "namespace__my" would result in the namespaced function selector "namespace__my__function", and would clash with a function "my__function" in namespace "namespace".)

fix(cli): mud set-version --link shouldn't fetch versions (#2000) (opens in a new tab) (@latticexyz/store-sync)

Postgres storage adapter now uses snake case for decoded table names and column names. This allows for better SQL ergonomics when querying these tables.

To avoid naming conflicts for now, schemas are still case-sensitive and need to be queried with double quotes. We may change this in the future with namespace validation (opens in a new tab).

Minor changes

feat(store): never allow empty FieldLayout (#2122) (opens in a new tab) (@latticexyz/store)

Removed allowEmpty option from FieldLayout.validate() as field layouts should never be empty.

feat(store): improve FieldLayout errors [N-03] (#2114) (opens in a new tab) (@latticexyz/store)

Improved error messages for invalid FieldLayouts

-error FieldLayoutLib_InvalidLength(uint256 length);
+error FieldLayoutLib_TooManyFields(uint256 numFields, uint256 maxFields);
+error FieldLayoutLib_TooManyDynamicFields(uint256 numFields, uint256 maxFields);
+error FieldLayoutLib_Empty();

Patch changes

fix(store): emit event after calling beforeSetRecord hook [L-02] (#2017) (opens in a new tab) (@latticexyz/store)

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.

refactor(world-modules): simplify getUniqueEntity call (#2161) (opens in a new tab) (@latticexyz/world-modules)

Removed IUniqueEntitySystem in favor of calling getUniqueEntity via world.call instead of the world function selector. This had a small gas improvement.

refactor(store,world): rename ambiguous elements [N-03] (#2091) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Renamed the requireNoCallback modifier to prohibitDirectCallback.

refactor(world): use _getSystem when fetching system addresses [N-11] (#2022) (opens in a new tab) (@latticexyz/world)

Optimised StoreRegistrationSystem and WorldRegistrationSystem by fetching individual fields instead of entire records where possible.

fix(world): inline debug constants [L-11] (#1976) (opens in a new tab) (@latticexyz/world)

Removed ROOT_NAMESPACE_STRING and ROOT_NAME_STRING exports in favor of inlining these constants, to avoid reuse as they're meant for internal error messages and debugging.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Refactored various files to specify integers in a hex base instead of decimals.

fix(store): do not render push and pop for static arrays, use static length [M-02] (#2175) (opens in a new tab) (@latticexyz/store)

Updated codegen to not render push and pop methods for static arrays. The length method now returns the hardcoded known length instead of calculating it like with a dynamic array.

fix(world): module supports world context consumer id [L-12] (#2032) (opens in a new tab) (@latticexyz/world)

Added the WorldContextConsumer interface ID to supportsInterface in the Module contract.

fix(world): limit call context of CoreSystem to delegatecall [C-02] (#2111) (opens in a new tab) (@latticexyz/world)

Systems are expected to be always called via the central World contract. Depending on whether it is a root or non-root system, the call is performed via delegatecall or call. Since Systems are expected to be stateless and only interact with the World state, it is not necessary to prevent direct calls to the systems. However, since the CoreSystem is known to always be registered as a root system in the World, it is always expected to be delegatecalled, so we made this expectation explicit by reverting if it is not delegatecalled.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/world)

Made the coreModule variable in WorldFactory immutable.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/world)

Removed the unnecessary extcodesize check from the Create2 library.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/store, @latticexyz/world)

Refactored ResourceId to use a global Solidity using statement.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/store, @latticexyz/world)

Refactored EIP165 usages to use the built-in interfaceId property instead of pre-defined constants.

fix(world): prevent initialising the world multiple times [L-05] (#2170) (opens in a new tab) (@latticexyz/world)

Added a table to track the CoreModule address the world was initialised with.

fix(store-sync): improve syncToZustand hydration speed (#2145) (opens in a new tab) (@latticexyz/store-sync)

Improved syncToZustand speed of hydrating from snapshot by only applying block logs once per block instead of once per log.

fix(store): revert if slice bound is invalid [L-10] (#2034) (opens in a new tab) (@latticexyz/store)

Added a custom error Store_InvalidBounds for when the start:end slice in getDynamicFieldSlice is invalid (it used to revert with the default overflow error)

refactor(store): order load function arguments [N-02] (#2033) (opens in a new tab) (@latticexyz/store)

Aligned the order of function arguments in the Storage library.

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)

fix(world): check namespace exists before balance transfer [L-03] (#2095) (opens in a new tab) (@latticexyz/world)

Namespace balances can no longer be transferred to non-existent namespaces.

fix(store): add missing FieldLayout and Schema validations [L-07] (#2046) (opens in a new tab) (@latticexyz/store)

Added more validation checks for FieldLayout and Schema.

fix(world): prevent misconfigured delegations, allow unregistering [L-04] (#2096) (opens in a new tab) (@latticexyz/world)

Prevented invalid delegations by performing full validation regardless of whether initCallData is empty. Added an unregisterDelegation function which allows explicit unregistration, as opposed of passing in zero bytes into registerDelegation.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/store, @latticexyz/world)

Refactored various Solidity files to not explicitly initialise variables to zero.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/store)

Refactored some Store functions to use a right bit mask instead of left.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/store)

Simplified a check in Slice.getSubslice.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/world)

Refactored WorldContext to get the world address from WorldContextConsumerLib instead of StoreSwitch.

refactor(store,world,world-modules): code suggestions [N-08] (#2140) (opens in a new tab) (@latticexyz/store)

Optimised the Schema.validate function to decrease gas use.


Version 2.0.0-next.15

Release date: Wed Jan 03 2024

Major changes

fix(store-sync,store-indexer): make last updated block number not null (#1972) (opens in a new tab) (@latticexyz/store-sync)

lastUpdatedBlockNumber columns in Postgres storage adapters are no longer nullable

feat(store-indexer): clean database if outdated (#1984) (opens in a new tab) (@latticexyz/store-sync)

Renamed singleton chain table to config table for clarity.

feat(store-sync, store-indexer): order logs by logIndex (#2037) (opens in a new tab) (@latticexyz/store-indexer, @latticexyz/store-sync)

The postgres indexer is now storing the logIndex of the last update of a record to be able to return the snapshot logs in the order they were emitted onchain.

feat(store-sync): fetch and store logs (#2003) (opens in a new tab) (@latticexyz/store-sync)

Previously, all store-sync strategies were susceptible to a potential memory leak where the stream that fetches logs from the RPC would get ahead of the stream that stores the logs in the provided storage adapter. We saw this most often when syncing to remote Postgres servers, where inserting records was much slower than we retrieving them from the RPC. In these cases, the stream would build up a backlog of items until the machine ran out of memory.

This is now fixed by waiting for logs to be stored before fetching the next batch of logs from the RPC. To make this strategy work, we no longer return blockLogs$ (stream of logs fetched from RPC but before they're stored) and instead just return storedBlockLogs$ (stream of logs fetched from RPC after they're stored).

feat(store-sync,store-indexer): schemaless indexer (#1965) (opens in a new tab) (@latticexyz/store-sync)

syncToPostgres from @latticexyz/store-sync/postgres now uses a single table to store all records in their bytes form (staticData, encodedLengths, and dynamicData), more closely mirroring onchain state and enabling more scalability and stability for automatic indexing of many worlds.

The previous behavior, where schemaful SQL tables are created and populated for each MUD table, has been moved to a separate @latticexyz/store-sync/postgres-decoded export bundle. This approach is considered less stable and is intended to be used for analytics purposes rather than hydrating clients. Some previous metadata columns on these tables have been removed in favor of the bytes records table as the source of truth for onchain state.

This overhaul is considered breaking and we recommend starting a fresh database when syncing with either of these strategies.

feat(store-sync): snake case postgres names in decoded tables (#1989) (opens in a new tab) (@latticexyz/store-sync)

Postgres storage adapter now uses snake case for decoded table names and column names. This allows for better SQL ergonomics when querying these tables.

To avoid naming conflicts for now, schemas are still case-sensitive and need to be queried with double quotes. We may change this in the future with namespace validation (opens in a new tab).

Minor changes

feat(store-sync,store-indexer): sync from getLogs indexer endpoint (#1973) (opens in a new tab) (@latticexyz/store-sync)

Refactored how we fetch snapshots from an indexer, preferring the new getLogs endpoint and falling back to the previous findAll if it isn't available. This refactor also prepares for an easier entry point for adding client caching of snapshots.

The initialState option for various sync methods (syncToPostgres, syncToRecs, etc.) is now deprecated in favor of initialBlockLogs. For now, we'll automatically convert initialState into initialBlockLogs, but if you want to update your code, you can do:

import { tablesWithRecordsToLogs } from "@latticexyz/store-sync";
 
const initialBlockLogs = {
  blockNumber: initialState.blockNumber,
  logs: tablesWithRecordsToLogs(initialState.tables),
};

feat(create-mud): remove window global usage in vanilla template (#1774) (opens in a new tab) (create-mud)

Replaced usage of window global in vanilla JS template with an event listener on the button.

feat(cli): add build command (#1990) (opens in a new tab) (@latticexyz/cli)

Added a mud build command that generates table libraries, system interfaces, and typed ABIs.

feat(store-sync,store-indexer): schemaless indexer (#1965) (opens in a new tab) (@latticexyz/common)

Added unique and groupBy array helpers to @latticexyz/common/utils.

import { unique } from "@latticexyz/common/utils";
 
unique([1, 2, 1, 4, 3, 2]);
// [1, 2, 4, 3]
import { groupBy } from "@latticexyz/common/utils";
 
const records = [
  { type: "cat", name: "Bob" },
  { type: "cat", name: "Spot" },
  { type: "dog", name: "Rover" },
];
Object.fromEntries(groupBy(records, (record) => record.type));
// {
//   "cat": [{ type: "cat", name: "Bob" }, { type: "cat", name: "Spot" }],
//   "dog: [{ type: "dog", name: "Rover" }]
// }

feat(store-sync,store-indexer): schemaless indexer (#1965) (opens in a new tab) (@latticexyz/store-indexer)

The findAll method is now considered deprecated in favor of a new getLogs method. This is only implemented in the Postgres indexer for now, with SQLite coming soon. The new getLogs method will be an easier and more robust data source to hydrate the client and other indexers and will allow us to add streaming updates from the indexer in the near future.

For backwards compatibility, findAll is now implemented on top of getLogs, with record key/value decoding done in memory at request time. This may not scale for large databases, so use wisely.

feat(store-indexer): clean database if outdated (#1984) (opens in a new tab) (@latticexyz/store-indexer)

When the Postgres indexer starts up, it will now attempt to detect if the database is outdated and, if so, cleans up all MUD-related schemas and tables before proceeding.

feat(store-indexer, store-sync): improve query performance and enable compression, add new api (#2026) (opens in a new tab) (@latticexyz/common)

docs: add changeset for zustand sync progress (#1931) (opens in a new tab) (@latticexyz/store-sync)

Added and populated syncProgress key in Zustand store for sync progress, like we do for RECS sync. This will let apps using syncToZustand render a loading state while initial client hydration is in progress.

const syncProgress = useStore((state) => state.syncProgress);
 
if (syncProgress.step !== SyncStep.LIVE) {
  return <>Loading ({Math.floor(syncProgress.percentage)}%)</>;
}

feat(store-sync,store-indexer): sync from getLogs indexer endpoint (#1973) (opens in a new tab) (@latticexyz/common)

Updated chunk types to use readonly arrays

feat(store-sync,store-indexer): sync from getLogs indexer endpoint (#1973) (opens in a new tab) (@latticexyz/store-indexer)

Added getLogs query support to sqlite indexer

feat(store-indexer, store-sync): improve query performance and enable compression, add new api (#2026) (opens in a new tab) (@latticexyz/store-indexer, @latticexyz/store-sync)

  • Improved query performance by 10x by moving from drizzle ORM to handcrafted SQL.
  • Moved away from trpc for more granular control over the transport layer. Added an /api/logs endpoint using the new query and gzip compression for 40x less data transferred over the wire. Deprecated the /trpc/getLogs and /trpc/findAll endpoints.
  • Added a createIndexerClient client for the new /api indexer API exported from @latticexyz/store-sync/indexer-client. The createIndexerClient export from @latticexyz/store-sync/trpc-indexer is deprecated.
- import { createIndexerClient } from "@latticexyz/store-sync/trpc-indexer";
+ import { createIndexerClient } from "@latticexyz/store-sync/indexer-client";
 
- const indexer = createIndexerClient({ url: "https://indexer.holesky.redstone.xyz/trpc" });
+ const indexer = createIndexerClient({ url: "https://indexer.holesky.redstone.xyz" });
 
- const snapshot = indexer.getLogs.query(options);
+ const snapshot = indexer.getLogs(options);

feat(store-indexer): return a "not found" error when no snapshot is found for a /api/logs request (#2043) (opens in a new tab) (@latticexyz/store-indexer)

The /api/logs indexer endpoint is now returning a 404 snapshot not found error when no snapshot is found for the provided filter instead of an empty 200 response.

fix(cli): add worldAddress to dev-contracts (#1892) (opens in a new tab) (@latticexyz/store-indexer)

Added STORE_ADDRESS environment variable to index only a specific MUD Store.

Patch changes

fix(store,world): exclude ERC165 interface ID from custom interface ID's [L-06] (#2014) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Exclude ERC165 interface ID from custom interface ID's.

fix(store-sync,store-indexer): make last updated block number not null (#1972) (opens in a new tab) (@latticexyz/store-indexer)

Records are now ordered by lastUpdatedBlockNumber at the Postgres SQL query level

fix(store): slice4 output should be bytes4 [M-03] (#2031) (opens in a new tab) (@latticexyz/store)

Changed the type of the output variable in the slice4 function to bytes4.

fix(cli): mud set-version --link shouldn't fetch versions (#2000) (opens in a new tab) (@latticexyz/cli)

Using mud set-version --link will no longer attempt to fetch the latest version from npm.

fix(store,world): fix mud config TS errors (#1974) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Fixed an issue where mud.config.ts source file was not included in the package, causing TS errors downstream.

feat(store-indexer): command to run decoded indexer (#2001) (opens in a new tab) (@latticexyz/store-indexer)

Added a script to run the decoded postgres indexer.

chore(store-indexer, store-sync): add explicit error logs (#2045) (opens in a new tab) (@latticexyz/store-indexer, @latticexyz/store-sync)

Added explicit error logs for unexpected situations. Previously all debug logs were going to stderr, which made it hard to find the unexpected errors. Now debug logs go to stdout and we can add explicit stderr logs.

chore(common): log benchmark to stderr (#2047) (opens in a new tab) (@latticexyz/common)

The benchmark util now logs to stdout instead of stderr.

chore(world): add explicit visibility to coreSystem [N-07] (#2029) (opens in a new tab) (@latticexyz/world)

Added explicit internal visibility to the coreSystem variable in CoreModule.

fix(world,world-modules): requireInterface correctly specifies ERC165 [M-02] (#2016) (opens in a new tab) (@latticexyz/world)

Fixed requireInterface to correctly specify ERC165.

feat(world): add isInstalled to Module (#2056) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/world)

Added isInstalled and requireNotInstalled helpers to Module base contract.

fix(store-sync): create table registration logs from indexer records (#1919) (opens in a new tab) (@latticexyz/store-sync)

createStoreSync now correctly creates table registration logs from indexer records.

chore(store-indexer): setup Sentry middleware in indexer (#2054) (opens in a new tab) (@latticexyz/store-indexer)

Added a Sentry middleware and SENTRY_DNS environment variable to the postgres indexer.

fix(world): register FunctionSignatures table [L-01] (#1841) (opens in a new tab) (@latticexyz/world)

World now correctly registers the FunctionSignatures table.

feat(store-indexer): replace fastify with koa (#2006) (opens in a new tab) (@latticexyz/store-indexer)

Replaced Fastify with Koa for store-indexer frontends

fix(create-mud): include .gitignore files in created projects (#1945) (opens in a new tab) (create-mud)

Templates now correctly include their respective .gitignore files

fix(cli): always rebuild IWorld ABI (#1929) (opens in a new tab) (@latticexyz/cli)

Deploys will now always rebuild IWorld.sol interface (a workaround for https://github.com/foundry-rs/foundry/issues/6241 (opens in a new tab))

build: allow use by TypeScript projects with bundler/node16 config (#2084) (opens in a new tab) (@latticexyz/abi-ts, @latticexyz/block-logs-stream, @latticexyz/common, @latticexyz/config, @latticexyz/dev-tools, @latticexyz/faucet, @latticexyz/gas-report, @latticexyz/noise, @latticexyz/phaserx, @latticexyz/protocol-parser, @latticexyz/react, @latticexyz/recs, @latticexyz/schema-type, @latticexyz/services, @latticexyz/store-sync, @latticexyz/store, @latticexyz/utils, @latticexyz/world-modules, @latticexyz/world)

TS packages now generate their respective .d.ts type definition files for better compatibility when using MUD with moduleResolution set to bundler or node16 and fixes issues around missing type declarations for dependent packages.

fix(store): onBeforeSpliceDynamicData receives the previous encoded lengths [M-01] (#2020) (opens in a new tab) (@latticexyz/store)

Fixed StoreCore to pass previousEncodedLengths into onBeforeSpliceDynamicData.

fix(store-indexer): disable prepared statements (#2058) (opens in a new tab) (@latticexyz/store-indexer)

Disabled prepared statements for the postgres indexer, which led to issues in combination with pgBouncer.

chore: pipe debug logs to stdout, add separate util to pipe to stderr (#2044) (opens in a new tab) (@latticexyz/abi-ts, @latticexyz/block-logs-stream, @latticexyz/cli, @latticexyz/common, @latticexyz/faucet, @latticexyz/store-indexer, @latticexyz/store-sync, @latticexyz/store)

Updated the debug util to pipe to stdout and added an additional util to explicitly pipe to stderr when needed.

chore(store-indexer): stringify filter in error log (#2048) (opens in a new tab) (@latticexyz/store-indexer)

The error log if no data is found in /api/logs is now stringifying the filter instead of logging [object Object].

fix(store): fix potential memory corruption [M-04] (#1978) (opens in a new tab) (@latticexyz/store)

Fixed M-04 Memory Corruption on Load From Storage It only affected external use of Storage.load with a memoryPointer argument

chore(store,world): remove unused imports [N-05] (#2028) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Removed unused imports from various files in the store and world packages.

fix(store-indexer): add postgres-decoded-indexer binary (#2062) (opens in a new tab) (@latticexyz/store-indexer)

Added a binary for the postgres-decoded indexer.

fix(world-modules): rename token address fields (#1986) (opens in a new tab) (@latticexyz/world-modules)

Renamed token address fields in ERC20 and ERC721 modules to tokenAddress

fix(react): trigger useComponentValue on deleted records (#1959) (opens in a new tab) (@latticexyz/react)

Fixed an issue where useComponentValue would not detect a change and re-render if the component value was immediately removed.

fix(store-sync): use dynamic data in postgres decoded indexer (#1983) (opens in a new tab) (@latticexyz/store-sync)

Fixed invalid value when decoding records in postgres-decoded storage adapter

fix(faucet): use MUD's sendTransaction for better nonce handling (#2080) (opens in a new tab) (@latticexyz/faucet)

Updated to use MUD's sendTransaction, which does a better of managing nonces for higher volumes of transactions.


Version 2.0.0-next.14

Release date: Fri Nov 10 2023

Major changes

feat(dev-tools): show zustand tables (#1891) (opens in a new tab) (@latticexyz/store-sync)

syncToZustand now uses tables argument to populate the Zustand store's tables key, rather than the on-chain table registration events. This means we'll no longer store data into Zustand you haven't opted into receiving (e.g. other namespaces).

feat(store-indexer): separate postgres indexer/frontend services (#1887) (opens in a new tab) (@latticexyz/store-indexer)

Separated frontend server and indexer service for Postgres indexer. Now you can run the Postgres indexer with one writer and many readers.

If you were previously using the postgres-indexer binary, you'll now need to run both postgres-indexer and postgres-frontend.

For consistency, the Postgres database logs are now disabled by default. If you were using these, please let us know so we can add them back in with an environment variable flag.

Minor changes

feat(cli): warn when contract is over or close to the size limit (#1894) (opens in a new tab) (@latticexyz/cli)

Deploys now validate contract size before deploying and warns when a contract is over or close to the size limit (24kb). This should help identify the most common cause of "evm revert" errors during system and module contract deploys.

fix(store): resolveUserTypes for static arrays (#1876) (opens in a new tab) (@latticexyz/schema-type)

Added isSchemaAbiType helper function to check and narrow an unknown string to the SchemaAbiType type

feat(dev-tools): show zustand tables (#1891) (opens in a new tab) (@latticexyz/dev-tools, create-mud)

Added Zustand support to Dev Tools:

const { syncToZustand } from "@latticexyz/store-sync";
const { mount as mountDevTools } from "@latticexyz/dev-tools";
 
const { useStore } = syncToZustand({ ... });
 
mountDevTools({
  ...
  useStore,
});

docs: add changeset for SystemboundDelegationControl (#1906) (opens in a new tab) (@latticexyz/world-modules)

Added a new delegation control called SystemboundDelegationControl that delegates control of a specific system for some maximum number of calls. It is almost identical to CallboundDelegationControl except the delegatee can call the system with any function and args.

feat(store-indexer): add env var to index only one store (#1886) (opens in a new tab) (@latticexyz/store-indexer)

Added STORE_ADDRESS environment variable to index only a specific MUD Store.

Patch changes

fix(create-mud): pin prettier-plugin-solidity (#1889) (opens in a new tab) (@latticexyz/common, create-mud)

Pinned prettier-plugin-solidity version to 1.1.3

fix(cli): add worldAddress to dev-contracts (#1892) (opens in a new tab) (@latticexyz/cli)

Added --worldAddress argument to dev-contracts CLI command so that you can develop against an existing world.

fix(store,world): explicit mud.config exports (#1900) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Added an explicit package export for mud.config

fix(store-sync): show TS error for non-existent tables when using zustand (#1896) (opens in a new tab) (@latticexyz/store-sync)

Fixed syncToZustand types so that non-existent tables give an error and never type instead of a generic Table type.

fix(store): resolveUserTypes for static arrays (#1876) (opens in a new tab) (@latticexyz/store)

Fixed resolveUserTypes for static arrays. resolveUserTypes is used by deploy, which prevented deploying tables with static arrays.

docs(faucet): fix default port in readme (#1835) (opens in a new tab) (@latticexyz/cli)

The mud test cli now exits with code 1 on test failure. It used to exit with code 0, which meant that CIs didn't notice test failures.


Version 2.0.0-next.13

Release date: Thu Nov 02 2023

Major changes

feat(utils): remove hash utils and ethers (#1783) (opens in a new tab) (@latticexyz/utils)

Removed keccak256 and keccak256Coord hash utils in favor of viem's keccak256 (opens in a new tab).

- import { keccak256 } from "@latticexyz/utils";
+ import { keccak256, toHex } from "viem";
 
- const hash = keccak256("some string");
+ const hash = keccak256(toHex("some string"));
- import { keccak256Coord } from "@latticexyz/utils";
+ import { encodeAbiParameters, keccak256, parseAbiParameters } from "viem";
 
  const coord = { x: 1, y: 1 };
- const hash = keccak256Coord(coord);
+ const hash = keccak256(encodeAbiParameters(parseAbiParameters("int32, int32"), [coord.x, coord.y]));

feat(store-indexer,store-sync): filter by table and key (#1794) (opens in a new tab) (@latticexyz/store-indexer)

Removed tableIds filter option in favor of the more flexible filters option that accepts tableId and an optional key0 and/or key1 to filter data by tables and keys.

If you were using an indexer client directly, you'll need to update your query:

  await indexer.findAll.query({
    chainId,
    address,
-   tableIds: ['0x...'],
+   filters: [{ tableId: '0x...' }],
  });

feat(create-mud): move react template to zustand, add react-ecs template (#1851) (opens in a new tab) (create-mud)

Replaced the react template with a basic task list app using the new Zustand storage adapter and sync method. This new template better demonstrates the different ways of building with MUD and has fewer concepts to learn (i.e. just tables and records, no more ECS).

For ECS-based React apps, you can use react-ecs template for the previous RECS storage adapter.

Minor changes

feat(create-mud): replace concurrently with mprocs (#1862) (opens in a new tab) (create-mud)

Updated templates to use mprocs (opens in a new tab) instead of concurrently (opens in a new tab) for running dev scripts.

feat(store-sync): extra table definitions (#1840) (opens in a new tab) (@latticexyz/store-sync)

Added an optional tables option to syncToRecs to allow you to sync from tables that may not be expressed by your MUD config. This will be useful for namespaced tables used by ERC20 (opens in a new tab) and ERC721 (opens in a new tab) token modules until the MUD config gains namespace support (opens in a new tab).

Here's how we use this in our example project with the KeysWithValue module:

syncToRecs({
  ...
  tables: {
    KeysWithValue: {
      namespace: "keywval",
      name: "Inventory",
      tableId: resourceToHex({ type: "table", namespace: "keywval", name: "Inventory" }),
      keySchema: {
        valueHash: { type: "bytes32" },
      },
      valueSchema: {
        keysWithValue: { type: "bytes32[]" },
      },
    },
  },
  ...
});

feat(world-modules): add ERC721 module (#1844) (opens in a new tab) (@latticexyz/world-modules)

Added the ERC721Module to @latticexyz/world-modules. This module allows the registration of ERC721 tokens in an existing World.

Important note: this module has not been audited yet, so any production use is discouraged for now.

import { PuppetModule } from "@latticexyz/world-modules/src/modules/puppet/PuppetModule.sol";
import { ERC721MetadataData } from "@latticexyz/world-modules/src/modules/erc721-puppet/tables/ERC721Metadata.sol";
import { IERC721Mintable } from "@latticexyz/world-modules/src/modules/erc721-puppet/IERC721Mintable.sol";
import { registerERC721 } from "@latticexyz/world-modules/src/modules/erc721-puppet/registerERC721.sol";
 
// The ERC721 module requires the Puppet module to be installed first
world.installModule(new PuppetModule(), new bytes(0));
 
// After the Puppet module is installed, new ERC721 tokens can be registered
IERC721Mintable token = registerERC721(world, "myERC721", ERC721MetadataData({ name: "Token", symbol: "TKN", baseURI: "" }));```

feat(world-modules): add puppet module (#1793) (opens in a new tab) (@latticexyz/world-modules)

Added the PuppetModule to @latticexyz/world-modules. The puppet pattern allows an external contract to be registered as an external interface for a MUD system. This allows standards like ERC20 (that require a specific interface and events to be emitted by a unique contract) to be implemented inside a MUD World.

The puppet serves as a proxy, forwarding all calls to the implementation system (also called the "puppet master"). The "puppet master" system can emit events from the puppet contract.

import { PuppetModule } from "@latticexyz/world-modules/src/modules/puppet/PuppetModule.sol";
import { createPuppet } from "@latticexyz/world-modules/src/modules/puppet/createPuppet.sol";
 
// Install the puppet module
world.installModule(new PuppetModule(), new bytes(0));
 
// Register a new puppet for any system
// The system must implement the `CustomInterface`,
// and the caller must own the system's namespace
CustomInterface puppet = CustomInterface(createPuppet(world, <systemId>));

feat(create-mud): enable MUD CLI debug logs (#1861) (opens in a new tab) (create-mud)

Enabled MUD CLI debug logs for all templates.

feat(store-indexer,store-sync): filter by table and key (#1794) (opens in a new tab) (@latticexyz/store-sync)

Added a filters option to store sync to allow filtering client data on tables and keys. Previously, it was only possible to filter on tableIds, but the new filter option allows for more flexible filtering by key.

If you are building a large MUD application, you can use positional keys as a way to shard data and make it possible to load only the data needed in the client for a particular section of your app. We're using this already in Sky Strife to load match-specific data into match pages without having to load data for all matches, greatly improving load time and client performance.

syncToRecs({
  ...
  filters: [{ tableId: '0x...', key0: '0x...' }],
});

The tableIds option is now deprecated and will be removed in the future, but is kept here for backwards compatibility.

feat(world-modules): add ERC20 module (#1789) (opens in a new tab) (@latticexyz/world-modules)

Added the ERC20Module to @latticexyz/world-modules. This module allows the registration of ERC20 tokens in an existing World.

Important note: this module has not been audited yet, so any production use is discouraged for now.

import { PuppetModule } from "@latticexyz/world-modules/src/modules/puppet/PuppetModule.sol";
import { IERC20Mintable } from "@latticexyz/world-modules/src/modules/erc20-puppet/IERC20Mintable.sol";
import { registerERC20 } from "@latticexyz/world-modules/src/modules/erc20-puppet/registerERC20.sol";
 
// The ERC20 module requires the Puppet module to be installed first
world.installModule(new PuppetModule(), new bytes(0));
 
// After the Puppet module is installed, new ERC20 tokens can be registered
IERC20Mintable token = registerERC20(world, "myERC20", ERC20MetadataData({ decimals: 18, name: "Token", symbol: "TKN" }));

feat(store-sync): sync to zustand (#1843) (opens in a new tab) (@latticexyz/store-sync)

Added a Zustand storage adapter and corresponding syncToZustand method for use in vanilla and React apps. It's used much like the other sync methods, except it returns a bound store and set of typed tables.

import { syncToZustand } from "@latticexyz/store-sync/zustand";
import config from "contracts/mud.config";
 
const { tables, useStore, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToZustand({
  config,
  ...
});
 
// in vanilla apps
const positions = useStore.getState().getRecords(tables.Position);
 
// in React apps
const positions = useStore((state) => state.getRecords(tables.Position));

This change will be shortly followed by an update to our templates that uses Zustand as the default client data store and sync method.

feat(store): add experimental config resolve helper (#1826) (opens in a new tab) (@latticexyz/common)

Added a mapObject helper to map the value of each property of an object to a new value.

Patch changes

fix(create-mud): set store address in PostDeploy script (#1817) (opens in a new tab) (create-mud)

Updated templates' PostDeploy script to set store address so that tables can be used directly inside PostDeploy.

fix(create-mud): workaround create-create-app templating (#1863) (opens in a new tab) (create-mud)

Fixed an issue when creating a new project from the react app, where React's expressions were overlapping with Handlebars expressions (used by our template command).

fix(cli): change import order so .env file is loaded first (#1860) (opens in a new tab) (@latticexyz/cli)

Changed mud CLI import order so that environment variables from the .env file are loaded before other imports.

fix(common,config): remove chalk usage (#1824) (opens in a new tab) (@latticexyz/common, @latticexyz/config)

Removed chalk usage from modules imported in client fix downstream client builds (vite in particular).


Version 2.0.0-next.12

Release date: Fri Oct 20 2023

Major changes

feat(store): default off storeArgument (#1741) (opens in a new tab) (@latticexyz/cli, @latticexyz/store, @latticexyz/world-modules, @latticexyz/world, create-mud)

Store config now defaults storeArgument: false for all tables. This means that table libraries, by default, will no longer include the extra functions with the _store argument. This default was changed to clear up the confusion around using table libraries in tests, PostDeploy scripts, etc.

If you are sure you need to manually specify a store when interacting with tables, you can still manually toggle it back on with storeArgument: true in the table settings of your MUD config.

If you want to use table libraries in PostDeploy.s.sol, you can add the following lines:

  import { Script } from "forge-std/Script.sol";
  import { console } from "forge-std/console.sol";
  import { IWorld } from "../src/codegen/world/IWorld.sol";
+ import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
 
  contract PostDeploy is Script {
    function run(address worldAddress) external {
+     StoreSwitch.setStoreAddress(worldAddress);
+
+     SomeTable.get(someKey);

feat(cli): declarative deployment (#1702) (opens in a new tab) (@latticexyz/cli)

deploy, test, dev-contracts were overhauled using a declarative deployment approach under the hood. Deploys are now idempotent and re-running them will introspect the world and figure out the minimal changes necessary to bring the world into alignment with its config: adding tables, adding/upgrading systems, changing access control, etc.

The following CLI arguments are now removed from these commands:

  • --debug (you can now adjust CLI output with DEBUG environment variable, e.g. DEBUG=mud:*)
  • --priorityFeeMultiplier (now calculated automatically)
  • --disableTxWait (everything is now parallelized with smarter nonce management)
  • --pollInterval (we now lean on viem defaults and we don't wait/poll until the very end of the deploy)

Most deployment-in-progress logs are now behind a debug (opens in a new tab) flag, which you can enable with a DEBUG=mud:* environment variable.

feat(world-modules): only install modules once (#1756) (opens in a new tab) (@latticexyz/world-modules)

Modules now revert with Module_AlreadyInstalled if attempting to install more than once with the same calldata.

This is a temporary workaround for our deploy pipeline. We'll make these install steps more idempotent in the future.

Minor changes

docs(world): add changeset for system call helpers (#1747) (opens in a new tab) (@latticexyz/world)

Added TS helpers for calling systems dynamically via the World.

  • encodeSystemCall for world.call

    worldContract.write.call(encodeSystemCall({
      abi: worldContract.abi,
      systemId: resourceToHex({ ... }),
      functionName: "registerDelegation",
      args: [ ... ],
    }));
  • encodeSystemCallFrom for world.callFrom

    worldContract.write.callFrom(encodeSystemCallFrom({
      abi: worldContract.abi,
      from: "0x...",
      systemId: resourceToHex({ ... }),
      functionName: "registerDelegation",
      args: [ ... ],
    }));
  • encodeSystemCalls for world.batchCall

    worldContract.write.batchCall(encodeSystemCalls(abi, [{
      systemId: resourceToHex({ ... }),
      functionName: "registerDelegation",
      args: [ ... ],
    }]));
  • encodeSystemCallsFrom for world.batchCallFrom

    worldContract.write.batchCallFrom(encodeSystemCallsFrom(abi, "0x...", [{
      systemId: resourceToHex({ ... }),
      functionName: "registerDelegation",
      args: [ ... ],
    }]));

feat(world-modules): only install modules once (#1756) (opens in a new tab) (@latticexyz/world)

Added a Module_AlreadyInstalled error to IModule.

feat(common): add sendTransaction, add mempool queue to nonce manager (#1717) (opens in a new tab) (@latticexyz/common)

  • Added a sendTransaction helper to mirror viem's sendTransaction, but with our nonce manager
  • Added an internal mempool queue to sendTransaction and writeContract for better nonce handling
  • Defaults block tag to pending for transaction simulation and transaction count (when initializing the nonce manager)

feat(cli): add --alwaysPostDeploy flag to deploys (#1765) (opens in a new tab) (@latticexyz/cli)

Added a --alwaysRunPostDeploy flag to deploys (deploy, test, dev-contracts commands) to always run PostDeploy.s.sol script after each deploy. By default, PostDeploy.s.sol is only run once after a new world is deployed.

This is helpful if you want to continue a deploy that may not have finished (due to an error or otherwise) or to run deploys with an idempotent PostDeploy.s.sol script.

feat(abi-ts): move logs to debug (#1736) (opens in a new tab) (@latticexyz/abi-ts)

Moves log output behind a debug flag. You can enable logging with DEBUG=abi-ts environment variable.

feat(cli): remove forge clean from deploy (#1759) (opens in a new tab) (@latticexyz/cli)

CLI deploy, test, dev-contracts no longer run forge clean before each deploy. We previously cleaned to ensure no outdated artifacts were checked into git (ABIs, typechain types, etc.). Now that all artifacts are gitignored, we can let forge use its cache again.

feat(common): clarify resourceId (hex) from resource (object) (#1706) (opens in a new tab) (@latticexyz/common)

Renames resourceIdToHex to resourceToHex and hexToResourceId to hexToResource, to better distinguish between a resource ID (hex value) and a resource reference (type, namespace, name).

- resourceIdToHex({ type: 'table', namespace: '', name: 'Position' });
+ resourceToHex({ type: 'table', namespace: '', name: 'Position' });
- hexToResourceId('0x...');
+ hexToResource('0x...');

Previous methods still exist but are now deprecated to ease migration and reduce breaking changes. These will be removed in a future version.

Also removes the previously deprecated and unused table ID utils (replaced by these resource ID utils).

feat(cli): remove .mudtest file in favor of env var (#1722) (opens in a new tab) (@latticexyz/cli, @latticexyz/common, @latticexyz/world)

Replaced temporary .mudtest file in favor of WORLD_ADDRESS environment variable when running tests with MudTest contract

feat(faucet,store-indexer): add k8s healthcheck endpoints (#1739) (opens in a new tab) (@latticexyz/faucet, @latticexyz/store-indexer)

Added /healthz and /readyz healthcheck endpoints for Kubernetes

feat(cli): add retries to deploy (#1766) (opens in a new tab) (@latticexyz/cli)

Transactions sent via deploy will now be retried a few times before giving up. This hopefully helps with large deploys on some chains.

Patch changes

fix(cli): don't bail dev-contracts during deploy failure (#1808) (opens in a new tab) (@latticexyz/cli)

dev-contracts will no longer bail when there was an issue with deploying (e.g. typo in contracts) and instead wait for file changes before retrying.

feat(store): parallelize table codegen (#1754) (opens in a new tab) (@latticexyz/store)

Parallelized table codegen. Also put logs behind debug flag, which can be enabled using the DEBUG=mud:* environment variable.

fix(cli): handle module already installed (#1769) (opens in a new tab) (@latticexyz/cli)

Deploys now continue if they detect a Module_AlreadyInstalled revert error.

fix(cli): deploy systems/modules before registering/installing them (#1767) (opens in a new tab) (@latticexyz/cli)

Changed deploy order so that system/module contracts are fully deployed before registering/installing them on the world.

fix(cli): run worldgen with deploy (#1807) (opens in a new tab) (@latticexyz/cli)

Deploy commands (deploy, dev-contracts, test) now correctly run worldgen to generate system interfaces before deploying.

feat(store): parallelize table codegen (#1754) (opens in a new tab) (@latticexyz/common)

Moved some codegen to use fs/promises for better parallelism.

fix(cli): support enums in deploy, only deploy modules/systems once (#1749) (opens in a new tab) (@latticexyz/cli)

Fixed a few issues with deploys:

  • properly handle enums in MUD config
  • only deploy each unique module/system once
  • waits for transactions serially instead of in parallel, to avoid RPC errors

feat(cli,create-mud): use forge cache (#1777) (opens in a new tab) (@latticexyz/cli, create-mud)

Sped up builds by using more of forge's cache.

Previously we'd build only what we needed because we would check in ABIs and other build artifacts into git, but that meant that we'd get a lot of forge cache misses. Now that we no longer need these files visible, we can take advantage of forge's caching and greatly speed up builds, especially incremental ones.

feat(cli): declarative deployment (#1702) (opens in a new tab) (@latticexyz/world)

With resource types in resource IDs (opens in a new tab), the World config no longer requires table and system names to be unique.

feat(common): clarify resourceId (hex) from resource (object) (#1706) (opens in a new tab) (@latticexyz/cli, @latticexyz/dev-tools, @latticexyz/store-sync)

Moved to new resource ID utils.


Version 2.0.0-next.11

Major changes

feat(cli): remove backup/restore/force options from set-version (#1687) (opens in a new tab) (@latticexyz/cli)

Removes .mudbackup file handling and --backup, --restore, and --force options from mud set-version command.

To revert to a previous MUD version, use git diff to find the version that you changed from and want to revert to and run pnpm mud set-version <prior-version> again.

Minor changes

feat(world-modules): add SystemSwitch util (#1665) (opens in a new tab) (@latticexyz/world-modules)

Since #1564 (opens in a new tab) the World can no longer call itself via an external call. This made the developer experience of calling other systems via root systems worse, since calls from root systems are executed from the context of the World. The recommended approach is to use delegatecall to the system if in the context of a root system, and an external call via the World if in the context of a non-root system. To bring back the developer experience of calling systems from other sysyems without caring about the context in which the call is executed, we added the SystemSwitch util.

- // Instead of calling the system via an external call to world...
- uint256 value = IBaseWorld(_world()).callMySystem();
 
+ // ...you can now use the `SystemSwitch` util.
+ // This works independent of whether used in a root system or non-root system.
+ uint256 value = abi.decode(SystemSwitch.call(abi.encodeCall(IBaseWorld.callMySystem, ()), (uint256));

Note that if you already know your system is always executed as non-root system, you can continue to use the approach of calling other systems via the IBaseWorld(world).

refactor(common): move createContract's internal write logic to writeContract (#1693) (opens in a new tab) (@latticexyz/common)

  • Moves contract write logic out of createContract into its own writeContract method so that it can be used outside of the contract instance, and for consistency with viem.
  • Deprecates createContract in favor of getContract for consistency with viem.
  • Reworks createNonceManager's BroadcastChannel setup and moves out the notion of a "nonce manager ID" to getNonceManagerId so we can create an internal cache with getNonceManager for use in writeContract.

If you were using the createNonceManager before, you'll just need to rename publicClient argument to client:

  const publicClient = createPublicClient({ ... });
- const nonceManager = createNonceManager({ publicClient, ... });
+ const nonceManager = createNonceManager({ client: publicClient, ... });

feat(gas-reporter): allow gas-reporter to parse stdin (#1688) (opens in a new tab) (@latticexyz/gas-report)

Allow the gas-report CLI to parse logs via stdin, so it can be used with custom test commands (e.g. mud test).

Usage:

# replace `forge test -vvv` with the custom test command
GAS_REPORTER_ENABLED=true forge test -vvv | pnpm gas-report --stdin

Patch changes

feat(store-sync): export postgres column type helpers (#1699) (opens in a new tab) (@latticexyz/store-sync)

Export postgres column type helpers from @latticexyz/store-sync.

fix(common): workaround for zero base fee (#1689) (opens in a new tab) (@latticexyz/common)

Adds viem workaround for zero base fee used by MUD's anvil config

fix(world): register store namespace during initialization (#1712) (opens in a new tab) (@latticexyz/world)

Register the store namespace in the CoreModule. Since namespaces are a World concept, registering the Store's internal tables does not automatically register the Store's namespace, so we do this manually during initialization in the CoreModule.

build: bump viem and abitype (#1684) (opens in a new tab) (@latticexyz/block-logs-stream, @latticexyz/cli, @latticexyz/common, @latticexyz/dev-tools, @latticexyz/faucet, @latticexyz/protocol-parser, @latticexyz/schema-type, @latticexyz/store-indexer, @latticexyz/store-sync, @latticexyz/store, create-mud)

Bump viem to 1.14.0 and abitype to 0.9.8

feat(gas-report): add more logs to stdin piping (#1694) (opens in a new tab) (@latticexyz/gas-report)

Pass through stdin logs in gas-report. Since the script piping in logs to gas-report can be long-running, it is useful to see its logs to know if it's stalling.

fix(protocol-parser): allow arbitrary key order when encoding values (#1674) (opens in a new tab) (@latticexyz/protocol-parser)

Allow arbitrary key order when encoding values


Version 2.0.0-next.10

Major changes

refactor(world): expose library for WorldContextConsumer (#1624) (opens in a new tab) (@latticexyz/world-modules, @latticexyz/world)

We now expose a WorldContextConsumerLib library with the same functionality as the WorldContextConsumer contract, but the ability to be used inside of internal libraries. We also renamed the WorldContextProvider library to WorldContextProviderLib for consistency.

Minor changes

docs: changeset for indexer/store sync table IDs param (#1662) (opens in a new tab) (@latticexyz/store-indexer, @latticexyz/store-sync)

Added a tableIds parameter to store sync methods and indexer to allow filtering data streams by table IDs. Store sync methods automatically include all internal table IDs from Store and World.

import { syncToRecs } from "@latticexyz/store-sync/recs";
import { resourceIdToHex } from "@latticexyz/common";
 
syncToRecs({
  ...
  tableIds: [resourceIdToHex(...)],
});
import { createIndexerClient } from "@latticexyz/store-sync/trpc-indexer";
import { resourceIdToHex } from "@latticexyz/common";
 
const client = createIndexerClient({ ... });
client.findAll({
  ...
  tableIds: [resourceIdToHex(...)],
});

feat(world): return world address from WorldFactory (#1675) (opens in a new tab) (@latticexyz/world)

Return address of the newly created World from WorldFactory.deployWorld.

Patch changes

fix(cli): fix table IDs for module install (#1663) (opens in a new tab) (@latticexyz/cli)

Fix table IDs for module install step of deploy

fix(cli): register namespace with namespaceId (#1619) (opens in a new tab) (@latticexyz/cli)

We fixed a bug in the deploy script that would cause the deployment to fail if a non-root namespace was used in the config.


Version 2.0.0-next.9

Major changes

feat(world): move interfaces/factories to root (#1606) (opens in a new tab) (@latticexyz/world)

Moves World interfaces and factories files for consistency with our other packages.

If you import any World interfaces or factories directly, you'll need to update the import path:

- import { IBaseWorld } from "@latticexyz/world/src/interfaces/IBaseWorld.sol";
+ import { IBaseWorld } from "@latticexyz/world/src/IBaseWorld.sol";
- import { IBaseWorld } from "@latticexyz/world/src/factories/WorldFactory.sol";
+ import { IBaseWorld } from "@latticexyz/world/src/WorldFactory.sol";

feat(world): prevent the World from calling itself (#1563) (opens in a new tab) (@latticexyz/world)

All World methods now revert if the World calls itself. The World should never need to externally call itself, since all internal table operations happen via library calls, and all root system operations happen via delegate call.

It should not be possible to make the World call itself as an external actor. If it were possible to make the World call itself, it would be possible to write to internal tables that only the World should have access to. As this is a very important invariance, we made it explicit in a requirement check in every World method, rather than just relying on making it impossible to trigger the World to call itself.

This is a breaking change for modules that previously used external calls to the World in the installRoot method. In the installRoot method, the World can only be called via delegatecall, and table operations should be performed via the internal table methods (e.g. _set instead of set).

Example for how to replace external calls to world in root systems / root modules (installRoot) with delegatecall:

+ import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol";
 
- world.grantAccess(tableId, address(hook));
+ (bool success, bytes memory returnData) = address(world).delegatecall(
+   abi.encodeCall(world.grantAccess, (tableId, address(hook)))
+ );
 
+ if (!success) revertWithBytes(returnData);

feat: rename schema to valueSchema (#1482) (opens in a new tab) (@latticexyz/cli, @latticexyz/protocol-parser, @latticexyz/store-sync, @latticexyz/store, create-mud)

Renamed all occurrences of schema where it is used as "value schema" to valueSchema to clearly distinguish it from "key schema". The only breaking change for users is the change from schema to valueSchema in mud.config.ts.

// mud.config.ts
export default mudConfig({
  tables: {
    CounterTable: {
      keySchema: {},
-     schema: {
+     valueSchema: {
        value: "uint32",
      },
    },
  }
}

refactor(world): move codegen files (#1592) (opens in a new tab) (@latticexyz/cli, @latticexyz/world-modules, @latticexyz/world)

Tables and interfaces in the world package are now generated to the codegen folder. This is only a breaking change if you imported tables or codegenerated interfaces from @latticexyz/world directly. If you're using the MUD CLI, the changed import paths are already integrated and no further changes are necessary.

- import { IBaseWorld } from "@latticexyz/world/src/interfaces/IBaseWorld.sol";
+ import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
 

refactor(store): always render field methods with suffix and conditionally without (#1550) (opens in a new tab) (@latticexyz/common)

  • Add renderWithFieldSuffix helper method to always render a field function with a suffix, and optionally render the same function without a suffix.
  • Remove methodNameSuffix from RenderField interface, because the suffix is now computed as part of renderWithFieldSuffix.

feat(store,): add splice events (#1354) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies.

If you've written your own sync logic or are interacting with Store calls directly, this is a breaking change. We have a few more breaking protocol changes upcoming, so you may hold off on upgrading until those land.

If you are using MUD's built-in tooling (table codegen, indexer, store sync, etc.), you don't have to make any changes except upgrading to the latest versions and deploying a fresh World.

  • The data field in each StoreSetRecord and StoreEphemeralRecord has been replaced with three new fields: staticData, encodedLengths, and dynamicData. This better reflects the on-chain state and makes it easier to perform modifications to the raw bytes. We recommend storing each of these fields individually in your off-chain storage of choice (indexer, client, etc.).

    - event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes data);
    + event StoreSetRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData);
     
    - event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes data);
    + event StoreEphemeralRecord(bytes32 tableId, bytes32[] keyTuple, bytes staticData, bytes32 encodedLengths, bytes dynamicData);
  • The StoreSetField event is now replaced by two new events: StoreSpliceStaticData and StoreSpliceDynamicData. Splicing allows us to perform efficient operations like push and pop, in addition to replacing a field value. We use two events because updating a dynamic-length field also requires updating the record's encodedLengths (aka PackedCounter).

    - event StoreSetField(bytes32 tableId, bytes32[] keyTuple, uint8 fieldIndex, bytes data);
    + event StoreSpliceStaticData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data);
    + event StoreSpliceDynamicData(bytes32 tableId, bytes32[] keyTuple, uint48 start, uint40 deleteCount, bytes data, bytes32 encodedLengths);

Similarly, Store setter methods (e.g. setRecord) have been updated to reflect the data to staticData, encodedLengths, and dynamicData changes. We'll be following up shortly with Store getter method changes for more gas efficient storage reads.

refactor(store): change argument order on Store_SpliceDynamicData and hooks for consistency (#1589) (opens in a new tab) (@latticexyz/store)

The argument order on Store_SpliceDynamicData, onBeforeSpliceDynamicData and onAfterSpliceDynamicData has been changed to match the argument order on Store_SetRecord, where the PackedCounter encodedLength field comes before the bytes dynamicData field.

IStore {
  event Store_SpliceDynamicData(
    ResourceId indexed tableId,
    bytes32[] keyTuple,
    uint48 start,
    uint40 deleteCount,
+   PackedCounter encodedLengths,
    bytes data,
-   PackedCounter encodedLengths
  );
}
 
IStoreHook {
  function onBeforeSpliceDynamicData(
    ResourceId tableId,
    bytes32[] memory keyTuple,
    uint8 dynamicFieldIndex,
    uint40 startWithinField,
    uint40 deleteCount,
+   PackedCounter encodedLengths,
    bytes memory data,
-   PackedCounter encodedLengths
  ) external;
 
  function onAfterSpliceDynamicData(
    ResourceId tableId,
    bytes32[] memory keyTuple,
    uint8 dynamicFieldIndex,
    uint40 startWithinField,
    uint40 deleteCount,
+   PackedCounter encodedLengths,
    bytes memory data,
-   PackedCounter encodedLengths
  ) external;
}

feat(store,): add splice events (#1354) (opens in a new tab) (@latticexyz/common, @latticexyz/protocol-parser)

readHex was moved from @latticexyz/protocol-parser to @latticexyz/common

feat(store,world): move hooks to bit flags (#1527) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Moved the registration of store hooks and systems hooks to bitmaps with bitwise operator instead of a struct.

- import { StoreHookLib } from "@latticexyz/src/StoreHook.sol";
+ import {
+   BEFORE_SET_RECORD,
+   BEFORE_SET_FIELD,
+   BEFORE_DELETE_RECORD
+ } from "@latticexyz/store/storeHookTypes.sol";
 
  StoreCore.registerStoreHook(
    tableId,
    subscriber,
-   StoreHookLib.encodeBitmap({
-     onBeforeSetRecord: true,
-     onAfterSetRecord: false,
-     onBeforeSetField: true,
-     onAfterSetField: false,
-     onBeforeDeleteRecord: true,
-     onAfterDeleteRecord: false
-   })
+   BEFORE_SET_RECORD | BEFORE_SET_FIELD | BEFORE_DELETE_RECORD
  );
- import { SystemHookLib } from "../src/SystemHook.sol";
+ import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "../src/systemHookTypes.sol";
 
  world.registerSystemHook(
    systemId,
    subscriber,
-   SystemHookLib.encodeBitmap({ onBeforeCallSystem: true, onAfterCallSystem: true })
+   BEFORE_CALL_SYSTEM | AFTER_CALL_SYSTEM
  );
 

feat(store,world): add splice hooks, expose spliceStaticData, spliceDynamicData (#1531) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

  • The IStoreHook interface was changed to replace onBeforeSetField and onAfterSetField with onBeforeSpliceStaticData, onAfterSpliceStaticData, onBeforeSpliceDynamicData and onAfterSpliceDynamicData.

    This new interface matches the new StoreSpliceStaticData and StoreSpliceDynamicData events, and avoids having to read the entire field from storage when only a subset of the field was updated (e.g. when pushing elements to a field).

    interface IStoreHook {
    - function onBeforeSetField(
    -   bytes32 tableId,
    -   bytes32[] memory keyTuple,
    -   uint8 fieldIndex,
    -   bytes memory data,
    -   FieldLayout fieldLayout
    - ) external;
     
    - function onAfterSetField(
    -   bytes32 tableId,
    -   bytes32[] memory keyTuple,
    -   uint8 fieldIndex,
    -   bytes memory data,
    -   FieldLayout fieldLayout
    - ) external;
     
    + function onBeforeSpliceStaticData(
    +   bytes32 tableId,
    +   bytes32[] memory keyTuple,
    +   uint48 start,
    +   uint40 deleteCount,
    +   bytes memory data
    + ) external;
     
    + function onAfterSpliceStaticData(
    +   bytes32 tableId,
    +   bytes32[] memory keyTuple,
    +   uint48 start,
    +   uint40 deleteCount,
    +   bytes memory data
    + ) external;
     
    + function onBeforeSpliceDynamicData(
    +   bytes32 tableId,
    +   bytes32[] memory keyTuple,
    +   uint8 dynamicFieldIndex,
    +   uint40 startWithinField,
    +   uint40 deleteCount,
    +   bytes memory data,
    +   PackedCounter encodedLengths
    + ) external;
     
    + function onAfterSpliceDynamicData(
    +   bytes32 tableId,
    +   bytes32[] memory keyTuple,
    +   uint8 dynamicFieldIndex,
    +   uint40 startWithinField,
    +   uint40 deleteCount,
    +   bytes memory data,
    +   PackedCounter encodedLengths
    + ) external;
    }
  • All calldata parameters on the IStoreHook interface were changed to memory, since the functions are called with memory from the World.

  • IStore exposes two new functions: spliceStaticData and spliceDynamicData.

    These functions provide lower level access to the operations happening under the hood in setField, pushToField, popFromField and updateInField and simplify handling the new splice hooks.

    StoreCore's internal logic was simplified to use the spliceStaticData and spliceDynamicData functions instead of duplicating similar logic in different functions.

    interface IStore {
      // Splice data in the static part of the record
      function spliceStaticData(
        bytes32 tableId,
        bytes32[] calldata keyTuple,
        uint48 start,
        uint40 deleteCount,
        bytes calldata data
      ) external;
     
      // Splice data in the dynamic part of the record
      function spliceDynamicData(
        bytes32 tableId,
        bytes32[] calldata keyTuple,
        uint8 dynamicFieldIndex,
        uint40 startWithinField,
        uint40 deleteCount,
        bytes calldata data
      ) external;
    }

feat: replace Schema with FieldLayout for contract internals (#1336) (opens in a new tab) (@latticexyz/cli, @latticexyz/protocol-parser, @latticexyz/store, @latticexyz/world)

  • Add FieldLayout, which is a bytes32 user-type similar to Schema.

    Both FieldLayout and Schema have the same kind of data in the first 4 bytes.

    • 2 bytes for total length of all static fields
    • 1 byte for number of static size fields
    • 1 byte for number of dynamic size fields

    But whereas Schema has SchemaType enum in each of the other 28 bytes, FieldLayout has static byte lengths in each of the other 28 bytes.

  • Replace Schema valueSchema with FieldLayout fieldLayout in Store and World contracts.

    FieldLayout is more gas-efficient because it already has lengths, and Schema has types which need to be converted to lengths.

  • Add getFieldLayout to IStore interface.

    There is no FieldLayout for keys, only for values, because key byte lengths aren't usually relevant on-chain. You can still use getKeySchema if you need key types.

  • Add fieldLayoutToHex utility to protocol-parser package.

  • Add constants.sol for constants shared between FieldLayout, Schema and PackedCounter.

refactor: separate data into staticData, encodedLengths, dynamicData in getRecord (#1532) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Store's getRecord has been updated to return staticData, encodedLengths, and dynamicData instead of a single data blob, to match the new behaviour of Store setter methods.

If you use codegenerated libraries, you will only need to update encode calls.

- bytes memory data = Position.encode(x, y);
+ (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData) = Position.encode(x, y);

feat(world): add FunctionSignatures offchain table (#1575) (opens in a new tab) (@latticexyz/cli, @latticexyz/world)

The registerRootFunctionSelector function's signature was changed to accept a string functionSignature parameter instead of a bytes4 functionSelector parameter. This change enables the World to store the function signatures of all registered functions in a FunctionSignatures offchain table, which will allow for the automatic generation of interfaces for a given World address in the future.

IBaseWorld {
  function registerRootFunctionSelector(
    ResourceId systemId,
-   bytes4 worldFunctionSelector,
+   string memory worldFunctionSignature,
    bytes4 systemFunctionSelector
  ) external returns (bytes4 worldFunctionSelector);
}

feat: move forge build + abi + abi-ts to out (#1483) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Store and World contract ABIs are now exported from the out directory. You'll need to update your imports like:

- import IBaseWorldAbi from "@latticexyz/world/abi/IBaseWorld.sol/IBaseWorldAbi.json";
+ import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorldAbi.json";

MudTest.sol was also moved to the World package. You can update your import like:

- import { MudTest } from "@latticexyz/store/src/MudTest.sol";
+ import { MudTest } from "@latticexyz/world/test/MudTest.t.sol";

feat(common,store): add support for user-defined types (#1566) (opens in a new tab) (@latticexyz/store)

These breaking changes only affect store utilities, you aren't affected if you use @latticexyz/cli codegen scripts.

  • Add remappings argument to the tablegen codegen function, so that it can read user-provided files.
  • In RenderTableOptions change the type of imports from RelativeImportDatum to ImportDatum, to allow passing absolute imports to the table renderer.
  • Add solidityUserTypes argument to several functions that need to resolve user or abi types: resolveAbiOrUserType, importForAbiOrUserType, getUserTypeInfo.
  • Add userTypes config option to MUD config, which takes user types mapped to file paths from which to import them.

refactor(store): always render field methods with suffix and conditionally without (#1550) (opens in a new tab) (@latticexyz/store)

  • Always render field methods with a suffix in tablegen (they used to not be rendered if field methods without a suffix were rendered).
  • Add withSuffixlessFieldMethods to RenderTableOptions, which indicates that field methods without a suffix should be rendered.

refactor(store,world): move around interfaces and base contracts (#1602) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

  • Moves Store events into its own IStoreEvents interface
  • Moves Store interfaces to their own files
  • Adds a StoreData abstract contract to initialize a Store and expose the Store version

If you're using MUD out of the box, you won't have to make any changes. You will only need to update if you're using any of the base Store interfaces.

feat(world): change registerFunctionSelector signature to accept system signature as a single string (#1574) (opens in a new tab) (@latticexyz/cli, @latticexyz/world)

The registerFunctionSelector function now accepts a single functionSignature string paramemer instead of separating function name and function arguments into separate parameters.

IBaseWorld {
  function registerFunctionSelector(
    ResourceId systemId,
-   string memory systemFunctionName,
-   string memory systemFunctionArguments
+   string memory systemFunctionSignature
  ) external returns (bytes4 worldFunctionSelector);
}

This is a breaking change if you were manually registering function selectors, e.g. in a PostDeploy.s.sol script or a module. To upgrade, simply replace the separate systemFunctionName and systemFunctionArguments parameters with a single systemFunctionSignature parameter.

  world.registerFunctionSelector(
    systemId,
-   systemFunctionName,
-   systemFunctionArguments,
+   string(abi.encodePacked(systemFunctionName, systemFunctionArguments))
  );

feat(store,world): replace ResourceSelector with ResourceId and WorldResourceId (#1544) (opens in a new tab) (@latticexyz/world)

All World methods acting on namespaces as resources have been updated to use ResourceId namespaceId as parameter instead of bytes14 namespace. The reason for this change is to make it clearer when a namespace is used as resource, as opposed to being part of another resource's ID.

+ import { ResourceId } from "@latticexyz/store/src/ResourceId.sol";
 
IBaseWorld {
- function registerNamespace(bytes14 namespace) external;
+ function registerNamespace(ResourceId namespaceId) external;
 
- function transferOwnership(bytes14 namespace, address newOwner) external;
+ function transferOwnership(ResourceId namespaceId, address newOwner) external;
 
- function transferBalanceToNamespace(bytes14 fromNamespace, bytes14 toNamespace, uint256 amount) external;
+ function transferBalanceToNamespace(ResourceId fromNamespaceId, ResourceId toNamespaceId, uint256 amount) external;
 
- function transferBalanceToAddress(bytes14 fromNamespace, address toAddress, uint256 amount) external;
+ function transferBalanceToAddress(ResourceId fromNamespaceId, address toAddress, uint256 amount) external;
}
 

feat: bump solidity to 0.8.21 (#1473) (opens in a new tab) (@latticexyz/cli, @latticexyz/common, @latticexyz/gas-report, @latticexyz/noise, @latticexyz/schema-type, @latticexyz/store, @latticexyz/world, create-mud)

Bump Solidity version to 0.8.21

feat(world): add batchCallFrom (#1594) (opens in a new tab) (@latticexyz/world)

  • IBaseWorld now has a batchCallFrom method, which allows system calls via callFrom to be executed in batch.
import { SystemCallFromData } from "@latticexyz/world/modules/core/types.sol";
 
interface IBaseWorld {
  function batchCallFrom(SystemCallFromData[] calldata systemCalls) external returns (bytes[] memory returnDatas);
}
  • The callBatch method of IBaseWorld has been renamed to batchCall to align better with the batchCallFrom method.
interface IBaseWorld {
- function callBatch(SystemCallData[] calldata systemCalls) external returns (bytes[] memory returnDatas);
+ function batchCall(SystemCallData[] calldata systemCalls) external returns (bytes[] memory returnDatas);
}

feat(store): codegen index and common files (#1318) (opens in a new tab) (@latticexyz/cli, @latticexyz/store, create-mud)

Renamed the default filename of generated user types from Types.sol to common.sol and the default filename of the generated table index file from Tables.sol to index.sol.

Both can be overridden via the MUD config:

export default mudConfig({
  /** Filename where common user types will be generated and imported from. */
  userTypesFilename: "common.sol",
  /** Filename where codegen index will be generated. */
  codegenIndexFilename: "index.sol",
});

Note: userTypesFilename was renamed from userTypesPath and .sol is not appended automatically anymore but needs to be part of the provided filename.

To update your existing project, update all imports from Tables.sol to index.sol and all imports from Types.sol to common.sol, or override the defaults in your MUD config to the previous values.

- import { Counter } from "../src/codegen/Tables.sol";
+ import { Counter } from "../src/codegen/index.sol";
- import { ExampleEnum } from "../src/codegen/Types.sol";
+ import { ExampleEnum } from "../src/codegen/common.sol";

feat(store,): add splice events (#1354) (opens in a new tab) (@latticexyz/dev-tools, @latticexyz/store-sync, create-mud)

We've updated Store events to be "schemaless", meaning there is enough information in each event to only need to operate on the bytes of each record to make an update to that record without having to first decode the record by its schema. This enables new kinds of indexers and sync strategies.

As such, we've replaced blockStorageOperations$ with storedBlockLogs$, a stream of simplified Store event logs after they've been synced to the configured storage adapter. These logs may not reflect exactly the events that are on chain when e.g. hydrating from an indexer, but they will still allow the client to "catch up" to the on-chain state of your tables.

feat(store,world): replace ephemeral tables with offchain tables (#1558) (opens in a new tab) (@latticexyz/block-logs-stream, @latticexyz/cli, @latticexyz/common, @latticexyz/dev-tools, @latticexyz/store-sync, @latticexyz/store, create-mud)

What used to be known as ephemeral table is now called offchain table. The previous ephemeral tables only supported an emitEphemeral method, which emitted a StoreSetEphemeralRecord event.

Now offchain tables support all regular table methods, except partial operations on dynamic fields (push, pop, update). Unlike regular tables they don't store data on-chain but emit the same events as regular tables (StoreSetRecord, StoreSpliceStaticData, StoreDeleteRecord), so their data can be indexed by offchain indexers/clients.

- EphemeralTable.emitEphemeral(value);
+ OffchainTable.set(value);

refactor(store,world): move store tables to store namespace, world tables to world namespace (#1601) (opens in a new tab) (@latticexyz/store-sync, @latticexyz/store, @latticexyz/world-modules, @latticexyz/world)

Moved store tables to the "store" namespace (previously "mudstore") and world tables to the "world" namespace (previously root namespace).

feat(store): rename events for readability and consistency with errors (#1577) (opens in a new tab) (@latticexyz/block-logs-stream, @latticexyz/dev-tools, @latticexyz/store-sync, @latticexyz/store)

Store events have been renamed for consistency and readability. If you're parsing Store events manually, you need to update your ABI. If you're using the MUD sync stack, the new events are already integrated and no further changes are necessary.

- event StoreSetRecord(
+ event Store_SetRecord(
    ResourceId indexed tableId,
    bytes32[] keyTuple,
    bytes staticData,
    bytes32 encodedLengths,
    bytes dynamicData
  );
- event StoreSpliceStaticData(
+ event Store_SpliceStaticData(
    ResourceId indexed tableId,
    bytes32[] keyTuple,
    uint48 start,
    uint40 deleteCount,
    bytes data
  );
- event StoreSpliceDynamicData(
+ event Store_SpliceDynamicData(
    ResourceId indexed tableId,
    bytes32[] keyTuple,
    uint48 start,
    uint40 deleteCount,
    bytes data,
    bytes32 encodedLengths
  );
- event StoreDeleteRecord(
+ event Store_DeleteRecord(
    ResourceId indexed tableId,
    bytes32[] keyTuple
  );

feat(store,world): replace ResourceSelector with ResourceId and WorldResourceId (#1544) (opens in a new tab) (@latticexyz/cli, @latticexyz/common, @latticexyz/config, @latticexyz/store)

  • ResourceSelector is replaced with ResourceId, ResourceIdLib, ResourceIdInstance, WorldResourceIdLib and WorldResourceIdInstance.

    Previously a "resource selector" was a bytes32 value with the first 16 bytes reserved for the resource's namespace, and the last 16 bytes reserved for the resource's name. Now a "resource ID" is a bytes32 value with the first 2 bytes reserved for the resource type, the next 14 bytes reserved for the resource's namespace, and the last 16 bytes reserved for the resource's name.

    Previously ResouceSelector was a library and the resource selector type was a plain bytes32. Now ResourceId is a user type, and the functionality is implemented in the ResourceIdInstance (for type) and WorldResourceIdInstance (for namespace and name) libraries. We split the logic into two libraries, because Store now also uses ResourceId and needs to be aware of resource types, but not of namespaces/names.

    - import { ResourceSelector } from "@latticexyz/world/src/ResourceSelector.sol";
    + import { ResourceId, ResourceIdInstance } from "@latticexyz/store/src/ResourceId.sol";
    + import { WorldResourceIdLib, WorldResourceIdInstance } from "@latticexyz/world/src/WorldResourceId.sol";
    + import { RESOURCE_SYSTEM } from "@latticexyz/world/src/worldResourceTypes.sol";
     
    - bytes32 systemId = ResourceSelector.from("namespace", "name");
    + ResourceId systemId = WorldResourceIdLib.encode(RESOURCE_SYSTEM, "namespace", "name");
     
    - using ResourceSelector for bytes32;
    + using WorldResourceIdInstance for ResourceId;
    + using ResourceIdInstance for ResourceId;
     
      systemId.getName();
      systemId.getNamespace();
    + systemId.getType();
     
  • All Store and World methods now use the ResourceId type for tableId, systemId, moduleId and namespaceId. All mentions of resourceSelector were renamed to resourceId or the more specific type (e.g. tableId, systemId)

    import { ResourceId } from "@latticexyz/store/src/ResourceId.sol";
     
    IStore {
      function setRecord(
    -   bytes32 tableId,
    +   ResourceId tableId,
        bytes32[] calldata keyTuple,
        bytes calldata staticData,
        PackedCounter encodedLengths,
        bytes calldata dynamicData,
        FieldLayout fieldLayout
      ) external;
     
      // Same for all other methods
    }
    import { ResourceId } from "@latticexyz/store/src/ResourceId.sol";
     
    IBaseWorld {
      function callFrom(
        address delegator,
    -   bytes32 resourceSelector,
    +   ResourceId systemId,
        bytes memory callData
      ) external payable returns (bytes memory);
     
      // Same for all other methods
    }

feat(store): indexed tableId in store events (#1520) (opens in a new tab) (@latticexyz/store)

Store events now use an indexed tableId. This adds ~100 gas per write, but means we our sync stack can filter events by table.

feat(world,store): add initialize method, initialize core tables in core module (#1472) (opens in a new tab) (@latticexyz/store)

  • StoreCore's initialize function is split into initialize (to set the StoreSwitch's storeAddress) and registerCoreTables (to register the Tables and StoreHooks tables). The purpose of this is to give consumers more granular control over the setup flow.

  • The StoreRead contract no longer calls StoreCore.initialize in its constructor. StoreCore consumers are expected to call StoreCore.initialize and StoreCore.registerCoreTable in their own setup logic.

feat(store): add internalType property to user types config for type inference (#1587) (opens in a new tab) (@latticexyz/cli, @latticexyz/common, @latticexyz/store)

Changed the userTypes property to accept { filePath: string, internalType: SchemaAbiType } to enable strong type inference from the config.

refactor(world,world-modules): move optional modules from world to world-modules package (#1591) (opens in a new tab) (@latticexyz/cli, @latticexyz/world, @latticexyz/world-modules)

All optional modules have been moved from @latticexyz/world to @latticexyz/world-modules. If you're using the MUD CLI, the import is already updated and no changes are necessary.

feat(store,world): polish store methods (#1581) (opens in a new tab) (@latticexyz/block-logs-stream, @latticexyz/cli, @latticexyz/common, @latticexyz/dev-tools, @latticexyz/store-sync, @latticexyz/store, @latticexyz/world, create-mud)

  • The external setRecord and deleteRecord methods of IStore no longer accept a FieldLayout as input, but load it from storage instead. This is to prevent invalid FieldLayout values being passed, which could cause the onchain state to diverge from the indexer state. However, the internal StoreCore library still exposes a setRecord and deleteRecord method that allows a FieldLayout to be passed. This is because StoreCore can only be used internally, so the FieldLayout value can be trusted and we can save the gas for accessing storage.

    interface IStore {
      function setRecord(
        ResourceId tableId,
        bytes32[] calldata keyTuple,
        bytes calldata staticData,
        PackedCounter encodedLengths,
        bytes calldata dynamicData,
    -   FieldLayout fieldLayout
      ) external;
     
      function deleteRecord(
        ResourceId tableId,
        bytes32[] memory keyTuple,
    -   FieldLayout fieldLayout
      ) external;
    }
  • The spliceStaticData method and Store_SpliceStaticData event of IStore and StoreCore no longer include deleteCount in their signature. This is because when splicing static data, the data after start is always overwritten with data instead of being shifted, so deleteCount is always the length of the data to be written.

     
    event Store_SpliceStaticData(
      ResourceId indexed tableId,
      bytes32[] keyTuple,
      uint48 start,
    - uint40 deleteCount,
      bytes data
    );
     
    interface IStore {
      function spliceStaticData(
        ResourceId tableId,
        bytes32[] calldata keyTuple,
        uint48 start,
    -   uint40 deleteCount,
        bytes calldata data
      ) external;
    }
  • The updateInField method has been removed from IStore, as it's almost identical to the more general spliceDynamicData. If you're manually calling updateInField, here is how to upgrade to spliceDynamicData:

    - store.updateInField(tableId, keyTuple, fieldIndex, startByteIndex, dataToSet, fieldLayout);
    + uint8 dynamicFieldIndex = fieldIndex - fieldLayout.numStaticFields();
    + store.spliceDynamicData(tableId, keyTuple, dynamicFieldIndex, uint40(startByteIndex), uint40(dataToSet.length), dataToSet);
  • All other methods that are only valid for dynamic fields (pushToField, popFromField, getFieldSlice) have been renamed to make this more explicit (pushToDynamicField, popFromDynamicField, getDynamicFieldSlice).

    Their fieldIndex parameter has been replaced by a dynamicFieldIndex parameter, which is the index relative to the first dynamic field (i.e. dynamicFieldIndex = fieldIndex - numStaticFields). The FieldLayout parameter has been removed, as it was only used to calculate the dynamicFieldIndex in the method.

    interface IStore {
    - function pushToField(
    + function pushToDynamicField(
        ResourceId tableId,
        bytes32[] calldata keyTuple,
    -   uint8 fieldIndex,
    +   uint8 dynamicFieldIndex,
        bytes calldata dataToPush,
    -   FieldLayout fieldLayout
      ) external;
     
    - function popFromField(
    + function popFromDynamicField(
        ResourceId tableId,
        bytes32[] calldata keyTuple,
    -   uint8 fieldIndex,
    +   uint8 dynamicFieldIndex,
        uint256 byteLengthToPop,
    -   FieldLayout fieldLayout
      ) external;
     
    - function getFieldSlice(
    + function getDynamicFieldSlice(
        ResourceId tableId,
        bytes32[] memory keyTuple,
    -   uint8 fieldIndex,
    +   uint8 dynamicFieldIndex,
    -   FieldLayout fieldLayout,
        uint256 start,
        uint256 end
      ) external view returns (bytes memory data);
    }
  • IStore has a new getDynamicFieldLength length method, which returns the byte length of the given dynamic field and doesn't require the FieldLayout.

    IStore {
    + function getDynamicFieldLength(
    +   ResourceId tableId,
    +   bytes32[] memory keyTuple,
    +   uint8 dynamicFieldIndex
    + ) external view returns (uint256);
    }
     
  • IStore now has additional overloads for getRecord, getField, getFieldLength and setField that don't require a FieldLength to be passed, but instead load it from storage.

  • IStore now exposes setStaticField and setDynamicField to save gas by avoiding the dynamic inference of whether the field is static or dynamic.

  • The getDynamicFieldSlice method no longer accepts reading outside the bounds of the dynamic field. This is to avoid returning invalid data, as the data of a dynamic field is not deleted when the record is deleted, but only its length is set to zero.

Minor changes

feat(dev-tools): update actions to display function name instead of callFrom (#1497) (opens in a new tab) (@latticexyz/dev-tools)

Improved rendering of transactions that make calls via World's call and callFrom methods

feat(faucet): add faucet service (#1517) (opens in a new tab) (@latticexyz/faucet)

New package to run your own faucet service. We'll use this soon for our testnet in place of @latticexyz/services.

To run the faucet server:

  • Add the package with pnpm add @latticexyz/faucet
  • Add a .env file that has a RPC_HTTP_URL and FAUCET_PRIVATE_KEY (or pass the environment variables into the next command)
  • Run pnpm faucet-server to start the server

You can also adjust the server's HOST (defaults to 0.0.0.0) and PORT (defaults to 3002). The tRPC routes are accessible under /trpc.

To connect a tRPC client, add the package with pnpm add @latticexyz/faucet and then use createClient:

import { createClient } from "@latticexyz/faucet";
 
const faucet = createClient({ url: "http://localhost:3002/trpc" });
 
await faucet.mutate.drip({ address: burnerAccount.address });

feat(world): add registerNamespaceDelegation for namespace-bound fallback delegation controls (#1590) (opens in a new tab) (@latticexyz/world)

It is now possible for namespace owners to register a fallback delegation control system for the namespace. This fallback delegation control system is used to verify a delegation in IBaseWorld.callFrom, after the user's individual and fallback delegations have been checked.

IBaseWorld {
  function registerNamespaceDelegation(
    ResourceId namespaceId,
    ResourceId delegationControlId,
    bytes memory initCallData
  ) external;
}

feat: move forge build + abi + abi-ts to out (#1483) (opens in a new tab) (create-mud)

Templates now use out for their forge build artifacts, including ABIs. If you have a project created from a previous template, you can update your packages/contracts/package.json with:

- "build:abi": "rimraf abi && forge build --extra-output-files abi --out abi --skip test script MudTest.sol",
- "build:abi-ts": "mud abi-ts --input 'abi/IWorld.sol/IWorld.abi.json' && prettier --write '**/*.abi.json.d.ts'",
+ "build:abi": "forge clean && forge build --skip test script",
+ "build:abi-ts": "mud abi-ts && prettier --write '**/*.abi.json.d.ts'",

And your packages/client/src/mud/setupNetwork with:

- import IWorldAbi from "contracts/abi/IWorld.sol/IWorld.abi.json";
+ import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json";

feat(common,store): add support for user-defined types (#1566) (opens in a new tab) (@latticexyz/common)

  • Add getRemappings to get foundry remappings as an array of [to, from] tuples.
  • Add extractUserTypes solidity parser utility to extract user-defined types.
  • Add loadAndExtractUserTypes helper to load and parse a solidity file, extracting user-defined types.

feat(store-indexer): run indexers with npx (#1526) (opens in a new tab) (@latticexyz/store-indexer)

You can now install and run @latticexyz/store-indexer from the npm package itself, without having to clone/build the MUD repo:

npm install @latticexyz/store-indexer
 
npm sqlite-indexer
# or
npm postgres-indexer

or

npx -p @latticexyz/store-indexer sqlite-indexer
# or
npx -p @latticexyz/store-indexer postgres-indexer

The binary will also load the nearby .env file for easier local configuration.

We've removed the CHAIN_ID requirement and instead require just a RPC_HTTP_URL or RPC_WS_URL or both. You can now also adjust the polling interval with POLLING_INTERVAL (defaults to 1000ms, which corresponds to MUD's default block time).

feat(store,): add splice events (#1354) (opens in a new tab) (@latticexyz/common)

spliceHex was added, which has a similar API as JavaScript's Array.prototype.splice (opens in a new tab), but for Hex strings.

spliceHex("0x123456", 1, 1, "0x0000"); // "0x12000056"

feat(protoocl-parser): add valueSchemaToFieldLayoutHex (#1476) (opens in a new tab) (@latticexyz/protocol-parser)

Adds valueSchemaToFieldLayoutHex helper

feat(store,world): emit Store/World versions (#1511) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Add protocol version with corresponding getter and event on deploy

world.worldVersion();
world.storeVersion(); // a World is also a Store
event HelloWorld(bytes32 indexed worldVersion);
event HelloStore(bytes32 indexed storeVersion);

feat(store): expose getStaticField and getDynamicField on IStore and use it in codegen tables (#1521) (opens in a new tab) (@latticexyz/cli, @latticexyz/store, @latticexyz/world)

StoreCore and IStore now expose specific functions for getStaticField and getDynamicField in addition to the general getField. Using the specific functions reduces gas overhead because more optimized logic can be executed.

interface IStore {
  /**
   * Get a single static field from the given tableId and key tuple, with the given value field layout.
   * Note: the field value is left-aligned in the returned bytes32, the rest of the word is not zeroed out.
   * Consumers are expected to truncate the returned value as needed.
   */
  function getStaticField(
    bytes32 tableId,
    bytes32[] calldata keyTuple,
    uint8 fieldIndex,
    FieldLayout fieldLayout
  ) external view returns (bytes32);
 
  /**
   * Get a single dynamic field from the given tableId and key tuple at the given dynamic field index.
   * (Dynamic field index = field index - number of static fields)
   */
  function getDynamicField(
    bytes32 tableId,
    bytes32[] memory keyTuple,
    uint8 dynamicFieldIndex
  ) external view returns (bytes memory);
}

refactor(store): inline logic in codegenned set method which uses struct (#1542) (opens in a new tab) (@latticexyz/store)

Add an optional namePrefix argument to renderRecordData, to support inlined logic in codegenned set method which uses a struct.

feat(store): indexed tableId in store events (#1520) (opens in a new tab) (@latticexyz/cli, @latticexyz/common, @latticexyz/store, @latticexyz/world)

Generated table libraries now have a set of functions prefixed with _ that always use their own storage for read/write. This saves gas for use cases where the functionality to dynamically determine which Store to use for read/write is not needed, e.g. root systems in a World, or when using Store without World.

We decided to continue to always generate a set of functions that dynamically decide which Store to use, so that the generated table libraries can still be imported by non-root systems.

library Counter {
  // Dynamically determine which store to write to based on the context
  function set(uint32 value) internal;
 
  // Always write to own storage
  function _set(uint32 value) internal;
 
  // ... equivalent functions for all other Store methods
}

feat(world,store): add initialize method, initialize core tables in core module (#1472) (opens in a new tab) (@latticexyz/cli, @latticexyz/world)

  • The World contract now has an initialize function, which can be called once by the creator of the World to install the core module. This change allows the registration of all core tables to happen in the CoreModule, so no table metadata has to be included in the World's bytecode.

    interface IBaseWorld {
      function initialize(IModule coreModule) public;
    }
  • The World contract now stores the original creator of the World in an immutable state variable. It is used internally to only allow the original creator to initialize the World in a separate transaction.

    interface IBaseWorld {
      function creator() external view returns (address);
    }
  • The deploy script is updated to use the World's initialize function to install the CoreModule instead of registerRootModule as before.

feat(world): add CallBatchSystem to core module (#1500) (opens in a new tab) (@latticexyz/world)

The World now has a callBatch method which allows multiple system calls to be batched into a single transaction.

import { SystemCallData } from "@latticexyz/world/modules/core/types.sol";
 
interface IBaseWorld {
  function callBatch(SystemCallData[] calldata systemCalls) external returns (bytes[] memory returnDatas);
}

Patch changes

fix(store-indexer): subscribe postgres indexer to stream (#1514) (opens in a new tab) (@latticexyz/store-indexer)

Fixes postgres indexer stopping sync after it catches up to the latest block.

fix: release bytecode on npm and import abi in cli deploy (#1490) (opens in a new tab) (@latticexyz/cli, @latticexyz/store, @latticexyz/world)

Include bytecode for World and Store in npm packages.

refactor(store,world): move test table config out of main table config (#1600) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Moved the test tables out of the main config in world and store and into their own separate config.

fix(common): always import relative sol files from ./ (#1585) (opens in a new tab) (@latticexyz/common)

Minor fix to resolving user types: solc doesn't like relative imports without ./, but is fine with relative imports from ./../, so we always append ./ to the relative path.

feat(store): compute FieldLayout at compile time (#1508) (opens in a new tab) (@latticexyz/cli, @latticexyz/store, @latticexyz/world)

The FieldLayout in table libraries is now generated at compile time instead of dynamically in a table library function. This significantly reduces gas cost in all table library functions.

feat(store): add Storage.loadField for optimized loading of 32 bytes or less from storage (#1512) (opens in a new tab) (@latticexyz/store)

Added Storage.loadField to optimize loading 32 bytes or less from storage (which is always the case when loading data for static fields).

refactor(store,world): prefix errors with library/contract name (#1568) (opens in a new tab) (@latticexyz/store, @latticexyz/world)

Prefixed all errors with their respective library/contract for improved debugging.

refactor(world): remove workaround in mud config (#1501) (opens in a new tab) (@latticexyz/world)

Remove a workaround for the internal InstalledModules table that is not needed anymore.

feat(world): rename funcSelectorAndArgs to callData (#1524) (opens in a new tab) (@latticexyz/world)

Renamed all funcSelectorAndArgs arguments to callData for clarity.

fix(faucet,store-indexer): fix invalid env message (#1546) (opens in a new tab) (@latticexyz/faucet, @latticexyz/store-indexer)

Improves error message when parsing env variables

feat(store,world): replace ResourceSelector with ResourceId and WorldResourceId (#1544) (opens in a new tab) (@latticexyz/world, @latticexyz/store)

The ResourceType table is removed. It was previously used to store the resource type for each resource ID in a World. This is no longer necessary as the resource type is now encoded in the resource ID (opens in a new tab).

To still be able to determine whether a given resource ID exists, a ResourceIds table has been added. The previous ResourceType table was part of World and missed tables that were registered directly via StoreCore.registerTable instead of via World.registerTable (e.g. when a table was registered as part of a root module). This problem is solved by the new table ResourceIds being part of Store.

StoreCore's hasTable function was removed in favor of using ResourceIds.getExists(tableId) directly.

- import { ResourceType } from "@latticexyz/world/src/tables/ResourceType.sol";
- import { StoreCore } from "@latticexyz/store/src/StoreCore.sol";
+ import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol";
 
- bool tableExists = StoreCore.hasTable(tableId);
+ bool tableExists = ResourceIds.getExists(tableId);
 
- bool systemExists = ResourceType.get(systemId) != Resource.NONE;
+ bool systemExists = ResourceIds.getExists(systemId);