diff --git a/.changeset/quick-years-juggle.md b/.changeset/quick-years-juggle.md new file mode 100644 index 0000000000..6415e1c24b --- /dev/null +++ b/.changeset/quick-years-juggle.md @@ -0,0 +1,10 @@ +--- +"@latticexyz/store": major +"@latticexyz/world": minor +--- + +- 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. diff --git a/packages/store/src/IStore.sol b/packages/store/src/IStore.sol index a7cfea1b8c..1799e17460 100644 --- a/packages/store/src/IStore.sol +++ b/packages/store/src/IStore.sol @@ -2,244 +2,7 @@ pragma solidity >=0.8.21; import { IStoreErrors } from "./IStoreErrors.sol"; -import { PackedCounter } from "./PackedCounter.sol"; -import { FieldLayout } from "./FieldLayout.sol"; -import { Schema } from "./Schema.sol"; -import { IStoreHook } from "./IStoreHook.sol"; -import { ResourceId } from "./ResourceId.sol"; - -interface IStoreRead { - event HelloStore(bytes32 indexed storeVersion); - - function storeVersion() external view returns (bytes32); - - function getFieldLayout(ResourceId tableId) external view returns (FieldLayout fieldLayout); - - function getValueSchema(ResourceId tableId) external view returns (Schema valueSchema); - - function getKeySchema(ResourceId tableId) external view returns (Schema keySchema); - - /** - * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, loading the field layout from storage - */ - function getRecord( - ResourceId tableId, - bytes32[] calldata keyTuple - ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); - - /** - * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, with the given field layout - */ - function getRecord( - ResourceId tableId, - bytes32[] calldata keyTuple, - FieldLayout fieldLayout - ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); - - /** - * Get a single field from the given tableId and key tuple, loading the field layout from storage - */ - function getField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex - ) external view returns (bytes memory data); - - /** - * Get a single field from the given tableId and key tuple, with the given field layout - */ - function getField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex, - FieldLayout fieldLayout - ) external view returns (bytes memory data); - - /** - * 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( - ResourceId 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( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 dynamicFieldIndex - ) external view returns (bytes memory); - - /** - * Get the byte length of a single field from the given tableId and key tuple, loading the field layout from storage - */ - function getFieldLength( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex - ) external view returns (uint256); - - /** - * Get the byte length of a single field from the given tableId and key tuple, with the given value field layout - */ - function getFieldLength( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 fieldIndex, - FieldLayout fieldLayout - ) external view returns (uint256); - - /** - * Get the byte length of a single dynamic field from the given tableId and key tuple - */ - function getDynamicFieldLength( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 dynamicFieldIndex - ) external view returns (uint256); - - /** - * Get a byte slice (including start, excluding end) of a single dynamic field from the given tableId and key tuple, with the given value field layout. - * The slice is unchecked and will return invalid data if `start`:`end` overflow. - */ - function getDynamicFieldSlice( - ResourceId tableId, - bytes32[] memory keyTuple, - uint8 dynamicFieldIndex, - uint256 start, - uint256 end - ) external view returns (bytes memory data); -} - -interface IStoreWrite { - event Store_SetRecord( - ResourceId indexed tableId, - bytes32[] keyTuple, - bytes staticData, - PackedCounter encodedLengths, - bytes dynamicData - ); - event Store_SpliceStaticData(ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, bytes data); - event Store_SpliceDynamicData( - ResourceId indexed tableId, - bytes32[] keyTuple, - uint48 start, - uint40 deleteCount, - PackedCounter encodedLengths, - bytes data - ); - event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple); - - // Set full record (including full dynamic data) - function setRecord( - ResourceId tableId, - bytes32[] calldata keyTuple, - bytes calldata staticData, - PackedCounter encodedLengths, - bytes calldata dynamicData - ) external; - - // Splice data in the static part of the record - function spliceStaticData( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint48 start, - bytes calldata data - ) external; - - // Splice data in the dynamic part of the record - function spliceDynamicData( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - uint40 startWithinField, - uint40 deleteCount, - bytes calldata data - ) external; - - // Set partial data at field index - function setField(ResourceId tableId, bytes32[] calldata keyTuple, uint8 fieldIndex, bytes calldata data) external; - - // Set partial data at field index - function setField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex, - bytes calldata data, - FieldLayout fieldLayout - ) external; - - function setStaticField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 fieldIndex, - bytes calldata data, - FieldLayout fieldLayout - ) external; - - function setDynamicField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - bytes calldata data - ) external; - - // Push encoded items to the dynamic field at field index - function pushToDynamicField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - bytes calldata dataToPush - ) external; - - // Pop byte length from the dynamic field at field index - function popFromDynamicField( - ResourceId tableId, - bytes32[] calldata keyTuple, - uint8 dynamicFieldIndex, - uint256 byteLengthToPop - ) external; - - // Set full record (including full dynamic data) - function deleteRecord(ResourceId tableId, bytes32[] memory keyTuple) external; -} - -/** - * The IStoreData interface includes methods for reading and writing table values. - * These methods are frequently invoked during runtime, so it is essential to prioritize - * optimizing their gas cost - */ -interface IStoreData is IStoreRead, IStoreWrite { - -} - -/** - * The IStoreRegistration interface includes methods for managing table field layouts, - * metadata, and hooks, which are usually called once in the setup phase of an application, - * making them less performance critical than the IStoreData methods. - */ -interface IStoreRegistration { - function registerTable( - ResourceId tableId, - FieldLayout fieldLayout, - Schema keySchema, - Schema valueSchema, - string[] calldata keyNames, - string[] calldata fieldNames - ) external; - - // Register hook to be called when a record or field is set or deleted - function registerStoreHook(ResourceId tableId, IStoreHook hookAddress, uint8 enabledHooksBitmap) external; - - // Unregister a hook for the given tableId - function unregisterStoreHook(ResourceId tableId, IStoreHook hookAddress) external; -} +import { IStoreData } from "./IStoreData.sol"; +import { IStoreRegistration } from "./IStoreRegistration.sol"; interface IStore is IStoreData, IStoreRegistration, IStoreErrors {} diff --git a/packages/store/src/IStoreData.sol b/packages/store/src/IStoreData.sol new file mode 100644 index 0000000000..31b66a3999 --- /dev/null +++ b/packages/store/src/IStoreData.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { IStoreRead } from "./IStoreRead.sol"; +import { IStoreWrite } from "./IStoreWrite.sol"; + +/** + * The IStoreData interface includes methods for reading and writing table values. + * These methods are frequently invoked during runtime, so it is essential to prioritize + * optimizing their gas cost + */ +interface IStoreData is IStoreRead, IStoreWrite { + event HelloStore(bytes32 indexed storeVersion); + + function storeVersion() external view returns (bytes32); +} diff --git a/packages/store/src/IStoreEvents.sol b/packages/store/src/IStoreEvents.sol new file mode 100644 index 0000000000..ff372e0eef --- /dev/null +++ b/packages/store/src/IStoreEvents.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { ResourceId } from "./ResourceId.sol"; +import { PackedCounter } from "./PackedCounter.sol"; + +interface IStoreEvents { + event Store_SetRecord( + ResourceId indexed tableId, + bytes32[] keyTuple, + bytes staticData, + PackedCounter encodedLengths, + bytes dynamicData + ); + event Store_SpliceStaticData(ResourceId indexed tableId, bytes32[] keyTuple, uint48 start, bytes data); + event Store_SpliceDynamicData( + ResourceId indexed tableId, + bytes32[] keyTuple, + uint48 start, + uint40 deleteCount, + PackedCounter encodedLengths, + bytes data + ); + event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple); +} diff --git a/packages/store/src/IStoreRead.sol b/packages/store/src/IStoreRead.sol new file mode 100644 index 0000000000..794eb8280f --- /dev/null +++ b/packages/store/src/IStoreRead.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { PackedCounter } from "./PackedCounter.sol"; +import { FieldLayout } from "./FieldLayout.sol"; +import { Schema } from "./Schema.sol"; +import { ResourceId } from "./ResourceId.sol"; + +interface IStoreRead { + function getFieldLayout(ResourceId tableId) external view returns (FieldLayout fieldLayout); + + function getValueSchema(ResourceId tableId) external view returns (Schema valueSchema); + + function getKeySchema(ResourceId tableId) external view returns (Schema keySchema); + + /** + * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, loading the field layout from storage + */ + function getRecord( + ResourceId tableId, + bytes32[] calldata keyTuple + ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); + + /** + * Get full record (all fields, static and dynamic data) for the given tableId and key tuple, with the given field layout + */ + function getRecord( + ResourceId tableId, + bytes32[] calldata keyTuple, + FieldLayout fieldLayout + ) external view returns (bytes memory staticData, PackedCounter encodedLengths, bytes memory dynamicData); + + /** + * Get a single field from the given tableId and key tuple, loading the field layout from storage + */ + function getField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex + ) external view returns (bytes memory data); + + /** + * Get a single field from the given tableId and key tuple, with the given field layout + */ + function getField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) external view returns (bytes memory data); + + /** + * 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( + ResourceId 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( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) external view returns (bytes memory); + + /** + * Get the byte length of a single field from the given tableId and key tuple, loading the field layout from storage + */ + function getFieldLength( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 fieldIndex + ) external view returns (uint256); + + /** + * Get the byte length of a single field from the given tableId and key tuple, with the given value field layout + */ + function getFieldLength( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 fieldIndex, + FieldLayout fieldLayout + ) external view returns (uint256); + + /** + * Get the byte length of a single dynamic field from the given tableId and key tuple + */ + function getDynamicFieldLength( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex + ) external view returns (uint256); + + /** + * Get a byte slice (including start, excluding end) of a single dynamic field from the given tableId and key tuple, with the given value field layout. + * The slice is unchecked and will return invalid data if `start`:`end` overflow. + */ + function getDynamicFieldSlice( + ResourceId tableId, + bytes32[] memory keyTuple, + uint8 dynamicFieldIndex, + uint256 start, + uint256 end + ) external view returns (bytes memory data); +} diff --git a/packages/store/src/IStoreRegistration.sol b/packages/store/src/IStoreRegistration.sol new file mode 100644 index 0000000000..58d73ab910 --- /dev/null +++ b/packages/store/src/IStoreRegistration.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { FieldLayout } from "./FieldLayout.sol"; +import { Schema } from "./Schema.sol"; +import { IStoreHook } from "./IStoreHook.sol"; +import { ResourceId } from "./ResourceId.sol"; + +/** + * The IStoreRegistration interface includes methods for managing table field layouts, + * metadata, and hooks, which are usually called once in the setup phase of an application, + * making them less performance critical than the methods. + */ +interface IStoreRegistration { + function registerTable( + ResourceId tableId, + FieldLayout fieldLayout, + Schema keySchema, + Schema valueSchema, + string[] calldata keyNames, + string[] calldata fieldNames + ) external; + + // Register hook to be called when a record or field is set or deleted + function registerStoreHook(ResourceId tableId, IStoreHook hookAddress, uint8 enabledHooksBitmap) external; + + // Unregister a hook for the given tableId + function unregisterStoreHook(ResourceId tableId, IStoreHook hookAddress) external; +} diff --git a/packages/store/src/IStoreWrite.sol b/packages/store/src/IStoreWrite.sol new file mode 100644 index 0000000000..99f6e3048c --- /dev/null +++ b/packages/store/src/IStoreWrite.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { IStoreEvents } from "./IStoreEvents.sol"; +import { PackedCounter } from "./PackedCounter.sol"; +import { FieldLayout } from "./FieldLayout.sol"; +import { ResourceId } from "./ResourceId.sol"; + +interface IStoreWrite is IStoreEvents { + // Set full record (including full dynamic data) + function setRecord( + ResourceId tableId, + bytes32[] calldata keyTuple, + bytes calldata staticData, + PackedCounter encodedLengths, + bytes calldata dynamicData + ) external; + + // Splice data in the static part of the record + function spliceStaticData( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint48 start, + bytes calldata data + ) external; + + // Splice data in the dynamic part of the record + function spliceDynamicData( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + uint40 startWithinField, + uint40 deleteCount, + bytes calldata data + ) external; + + // Set partial data at field index + function setField(ResourceId tableId, bytes32[] calldata keyTuple, uint8 fieldIndex, bytes calldata data) external; + + // Set partial data at field index + function setField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + bytes calldata data, + FieldLayout fieldLayout + ) external; + + function setStaticField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 fieldIndex, + bytes calldata data, + FieldLayout fieldLayout + ) external; + + function setDynamicField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + bytes calldata data + ) external; + + // Push encoded items to the dynamic field at field index + function pushToDynamicField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + bytes calldata dataToPush + ) external; + + // Pop byte length from the dynamic field at field index + function popFromDynamicField( + ResourceId tableId, + bytes32[] calldata keyTuple, + uint8 dynamicFieldIndex, + uint256 byteLengthToPop + ) external; + + // Set full record (including full dynamic data) + function deleteRecord(ResourceId tableId, bytes32[] memory keyTuple) external; +} diff --git a/packages/store/src/StoreCore.sol b/packages/store/src/StoreCore.sol index 22c3d17974..dcee036261 100644 --- a/packages/store/src/StoreCore.sol +++ b/packages/store/src/StoreCore.sol @@ -51,8 +51,6 @@ library StoreCore { * Consumers must call this function in their constructor. */ function initialize() internal { - emit HelloStore(STORE_VERSION); - // StoreSwitch uses the storeAddress to decide where to write data to. // If StoreSwitch is called in the context of a Store contract (storeAddress == address(this)), // StoreSwitch uses internal methods to write data instead of external calls. diff --git a/packages/store/src/StoreData.sol b/packages/store/src/StoreData.sol new file mode 100644 index 0000000000..b5b99f0616 --- /dev/null +++ b/packages/store/src/StoreData.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.21; + +import { STORE_VERSION } from "./version.sol"; +import { IStoreData } from "./IStoreData.sol"; +import { StoreRead } from "./StoreRead.sol"; +import { StoreCore } from "./StoreCore.sol"; + +abstract contract StoreData is IStoreData, StoreRead { + constructor() { + StoreCore.initialize(); + emit HelloStore(STORE_VERSION); + } + + function storeVersion() public pure returns (bytes32) { + return STORE_VERSION; + } +} diff --git a/packages/store/src/StoreRead.sol b/packages/store/src/StoreRead.sol index a9720b6907..f64d66b803 100644 --- a/packages/store/src/StoreRead.sol +++ b/packages/store/src/StoreRead.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.21; import { STORE_VERSION } from "./version.sol"; -import { IStoreRead } from "./IStore.sol"; +import { IStoreRead } from "./IStoreRead.sol"; import { StoreCore } from "./StoreCore.sol"; import { FieldLayout } from "./FieldLayout.sol"; import { Schema } from "./Schema.sol"; @@ -10,10 +10,6 @@ import { PackedCounter } from "./PackedCounter.sol"; import { ResourceId } from "./ResourceId.sol"; contract StoreRead is IStoreRead { - function storeVersion() public pure returns (bytes32) { - return STORE_VERSION; - } - function getFieldLayout(ResourceId tableId) public view virtual returns (FieldLayout fieldLayout) { fieldLayout = StoreCore.getFieldLayout(tableId); } diff --git a/packages/store/test/StoreHook.t.sol b/packages/store/test/StoreHook.t.sol index f4d03a7471..889c8c5bfa 100644 --- a/packages/store/test/StoreHook.t.sol +++ b/packages/store/test/StoreHook.t.sol @@ -8,7 +8,7 @@ import { EchoSubscriber } from "./EchoSubscriber.sol"; import { RevertSubscriber } from "./RevertSubscriber.sol"; import { Hook, HookLib } from "../src/Hook.sol"; -import { IStoreHook } from "../src/IStore.sol"; +import { IStoreHook } from "../src/IStoreHook.sol"; import { PackedCounter } from "../src/PackedCounter.sol"; import { FieldLayout } from "../src/FieldLayout.sol"; import { ResourceId, ResourceIdLib } from "../src/ResourceId.sol"; diff --git a/packages/store/test/StoreMock.sol b/packages/store/test/StoreMock.sol index 590a53f033..f8427117f8 100644 --- a/packages/store/test/StoreMock.sol +++ b/packages/store/test/StoreMock.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { IStore, IStoreHook } from "../src/IStore.sol"; +import { IStore } from "../src/IStore.sol"; +import { IStoreHook } from "../src/IStoreHook.sol"; +import { StoreData } from "../src/StoreData.sol"; import { PackedCounter } from "../src/PackedCounter.sol"; import { StoreCore } from "../src/StoreCore.sol"; import { Schema } from "../src/Schema.sol"; @@ -12,7 +14,7 @@ import { ResourceId } from "../src/ResourceId.sol"; /** * StoreMock is a contract wrapper around the StoreCore library for testing purposes. */ -contract StoreMock is IStore, StoreRead { +contract StoreMock is IStore, StoreData { constructor() { StoreCore.initialize(); StoreCore.registerCoreTables(); diff --git a/packages/world/src/World.sol b/packages/world/src/World.sol index 838ba41e1f..9c7fd2277e 100644 --- a/packages/world/src/World.sol +++ b/packages/world/src/World.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.21; -import { StoreRead } from "@latticexyz/store/src/StoreRead.sol"; -import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; -import { IStoreData } from "@latticexyz/store/src/IStore.sol"; +import { StoreData } from "@latticexyz/store/src/StoreData.sol"; +import { IStore } from "@latticexyz/store/src/IStore.sol"; import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { Bytes } from "@latticexyz/store/src/Bytes.sol"; import { Schema } from "@latticexyz/store/src/Schema.sol"; @@ -36,7 +35,7 @@ import { FunctionSelectors } from "./codegen/tables/FunctionSelectors.sol"; import { Balances } from "./codegen/tables/Balances.sol"; import { CORE_MODULE_NAME } from "./modules/core/constants.sol"; -contract World is StoreRead, IStoreData, IWorldKernel { +contract World is StoreData, IWorldKernel { using WorldResourceIdInstance for ResourceId; address public immutable creator; @@ -47,7 +46,6 @@ contract World is StoreRead, IStoreData, IWorldKernel { constructor() { creator = msg.sender; - StoreCore.initialize(); emit HelloWorld(WORLD_VERSION); } diff --git a/packages/world/test/World.t.sol b/packages/world/test/World.t.sol index 70c34d2fff..d6331b04fe 100644 --- a/packages/world/test/World.t.sol +++ b/packages/world/test/World.t.sol @@ -8,7 +8,7 @@ import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol" import { IStoreHook, STORE_HOOK_INTERFACE_ID } from "@latticexyz/store/src/IStoreHook.sol"; import { StoreCore, StoreCoreInternal } from "@latticexyz/store/src/StoreCore.sol"; -import { IStoreErrors } from "@latticexyz/store/src/IStore.sol"; +import { IStoreErrors } from "@latticexyz/store/src/IStoreErrors.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { FieldLayout, FieldLayoutLib } from "@latticexyz/store/src/FieldLayout.sol"; import { FieldLayoutEncodeHelper } from "@latticexyz/store/test/FieldLayoutEncodeHelper.sol"; diff --git a/packages/world/test/WorldDynamicUpdate.t.sol b/packages/world/test/WorldDynamicUpdate.t.sol index 5d7f930481..1427669b85 100644 --- a/packages/world/test/WorldDynamicUpdate.t.sol +++ b/packages/world/test/WorldDynamicUpdate.t.sol @@ -6,7 +6,7 @@ import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol"; import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol"; -import { IStoreHook } from "@latticexyz/store/src/IStore.sol"; +import { IStoreHook } from "@latticexyz/store/src/IStoreHook.sol"; import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol";