Skip to content

@latticexyz/[email protected]

Pre-release
Pre-release
Compare
Choose a tag to compare
@github-actions github-actions released this 25 Sep 18:43
· 1133 commits to main since this release
bdf2882

Major Changes

  • #1606 77dce993 Thanks @holic! - 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";
  • #1563 748f4588 Thanks @alvrs! - 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);
  • #1592 c07fa021 Thanks @alvrs! - 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";
    
  • #1354 331dbfdc Thanks @dk1a! - 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.

  • #1527 759514d8 Thanks @holic! - 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
      );
    
  • #1531 d5094a24 Thanks @alvrs! - - 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;
      }
  • #1336 de151fec Thanks @dk1a! - - 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.

  • #1532 ae340b2b Thanks @dk1a! - 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);
  • #1575 e5d208e4 Thanks @alvrs! - 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);
    }
  • #1483 83583a50 Thanks @holic! - 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";
  • #1574 31ffc9d5 Thanks @alvrs! - 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))
      );
  • #1544 5e723b90 Thanks @alvrs! - 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;
    }
    
  • #1473 92de5998 Thanks @holic! - Bump Solidity version to 0.8.21

  • #1594 5741d53d Thanks @alvrs! - - 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);
    }
  • #1601 1890f1a0 Thanks @alvrs! - Moved store tables to the "store" namespace (previously "mudstore") and world tables to the "world" namespace (previously root namespace).

  • #1591 251170e1 Thanks @alvrs! - 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.

  • #1581 cea754dd Thanks @alvrs! - - 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

  • #1590 1f80a0b5 Thanks @alvrs! - 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;
    }
  • #1602 672d05ca Thanks @holic! - - 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.

  • #1511 9b43029c Thanks @holic! - 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);
  • #1472 c049c23f Thanks @alvrs! - - 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.

  • #1500 95c59b20 Thanks @yonadaaa! - 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

  • #1490 aea67c58 Thanks @alvrs! - Include bytecode for World and Store in npm packages.

  • #1600 90e4161b Thanks @alvrs! - Moved the test tables out of the main config in world and store and into their own separate config.

  • #1508 211be2a1 Thanks @Boffee! - 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.

  • #1568 d0878928 Thanks @alvrs! - Prefixed all errors with their respective library/contract for improved debugging.

  • #1501 4c7fd3eb Thanks @alvrs! - Remove a workaround for the internal InstalledModules table that is not needed anymore.

  • #1524 a0341daf Thanks @holic! - Renamed all funcSelectorAndArgs arguments to callData for clarity.

  • #1544 5e723b90 Thanks @alvrs! - 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.

    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);
  • #1492 6e66c5b7 Thanks @alvrs! - Renamed all occurrences of key where it is used as "key tuple" to keyTuple.
    This is only a breaking change for consumers who manually decode Store events, but not for consumers who use the MUD libraries.

    event StoreSetRecord(
      bytes32 tableId,
    - bytes32[] key,
    + bytes32[] keyTuple,
      bytes data
    );
    
    event StoreSetField(
      bytes32 tableId,
    - bytes32[] key,
    + bytes32[] keyTuple,
      uint8 fieldIndex,
      bytes data
    );
    
    event StoreDeleteRecord(
      bytes32 tableId,
    - bytes32[] key,
    + bytes32[] keyTuple,
    );
    
    event StoreEphemeralRecord(
      bytes32 tableId,
    - bytes32[] key,
    + bytes32[] keyTuple,
      bytes data
    );
  • #1452 f1cd43bf Thanks @alvrs! - Register Delegations table in the CoreModule

  • #1586 22ee4470 Thanks @alvrs! - All Store and World tables now use the appropriate user-types for ResourceId, FieldLayout and Schema to avoid manual wrap/unwrap.

  • #1509 be313068 Thanks @Boffee! - Optimized the StoreCore hash function determining the data location to use less gas.

  • #1521 55ab88a6 Thanks @alvrs! - 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);
    }
  • #1513 708b49c5 Thanks @Boffee! - 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
    }
  • #1569 22ba7b67 Thanks @alvrs! - Simplified a couple internal constants used for bitshifting.

  • Updated dependencies [aea67c58, 07dd6f32, 90e4161b, 65c9546c, 331dbfdc, f9f9609e, 331dbfdc, 759514d8, d5094a24, 0b8ce3f2, de151fec, ae340b2b, 211be2a1, 0f3e2e02, d0878928, 83583a50, 5e723b90, 6573e38e, 44a5432a, 6e66c5b7, 65c9546c, 44a5432a, 672d05ca, 63831a26, 331dbfdc, 92de5998, 22ee4470, be313068, ac508bf1, bfcb293d, 1890f1a0, 9b43029c, 55ab88a6, af639a26, 5e723b90, 99ab9cd6, c049c23f, 80dd6992, 24a6cd53, 708b49c5, 22ba7b67, c4f49240, cea754dd]: